@@ -27,25 +27,24 @@ internal sealed class NamedPipeConnectionListener : IConnectionListener
27
27
private readonly MemoryPool < byte > _memoryPool ;
28
28
private readonly PipeOptions _inputOptions ;
29
29
private readonly PipeOptions _outputOptions ;
30
- private readonly Mutex _mutex ;
30
+ private readonly NamedPipeServerStreamPoolPolicy _poolPolicy ;
31
31
private Task ? _completeListeningTask ;
32
32
private int _disposed ;
33
33
34
34
public NamedPipeConnectionListener (
35
35
NamedPipeEndPoint endpoint ,
36
36
NamedPipeTransportOptions options ,
37
37
ILoggerFactory loggerFactory ,
38
- ObjectPoolProvider objectPoolProvider ,
39
- Mutex mutex )
38
+ ObjectPoolProvider objectPoolProvider )
40
39
{
41
40
_log = loggerFactory . CreateLogger ( "Microsoft.AspNetCore.Server.Kestrel.Transport.NamedPipes" ) ;
42
41
_endpoint = endpoint ;
43
42
_options = options ;
44
- _mutex = mutex ;
45
43
_memoryPool = options . MemoryPoolFactory ( ) ;
46
44
_listeningToken = _listeningTokenSource . Token ;
47
45
// Have to create the pool here (instead of DI) because the pool is specific to an endpoint.
48
- _namedPipeServerStreamPool = objectPoolProvider . Create ( new NamedPipeServerStreamPoolPolicy ( endpoint , options ) ) ;
46
+ _poolPolicy = new NamedPipeServerStreamPoolPolicy ( endpoint , options ) ;
47
+ _namedPipeServerStreamPool = objectPoolProvider . Create ( _poolPolicy ) ;
49
48
50
49
// The OS maintains a backlog of clients that are waiting to connect, so the app queue only stores a single connection.
51
50
// We want to have a queue plus a background task that populates the queue, rather than creating NamedPipeServerStream
@@ -77,6 +76,7 @@ public void Start()
77
76
{
78
77
// Start first stream inline to catch creation errors.
79
78
var initialStream = _namedPipeServerStreamPool . Get ( ) ;
79
+ _poolPolicy . SetFirstPipeStarted ( ) ;
80
80
81
81
listeningTasks [ i ] = Task . Run ( ( ) => StartAsync ( initialStream ) ) ;
82
82
}
@@ -170,7 +170,6 @@ public async ValueTask DisposeAsync()
170
170
}
171
171
172
172
_listeningTokenSource . Dispose ( ) ;
173
- _mutex . Dispose ( ) ;
174
173
if ( _completeListeningTask != null )
175
174
{
176
175
await _completeListeningTask ;
@@ -185,6 +184,7 @@ private sealed class NamedPipeServerStreamPoolPolicy : IPooledObjectPolicy<Named
185
184
{
186
185
private readonly NamedPipeEndPoint _endpoint ;
187
186
private readonly NamedPipeTransportOptions _options ;
187
+ private bool _hasFirstPipeStarted ;
188
188
189
189
public NamedPipeServerStreamPoolPolicy ( NamedPipeEndPoint endpoint , NamedPipeTransportOptions options )
190
190
{
@@ -196,6 +196,14 @@ public NamedPipeServerStream Create()
196
196
{
197
197
NamedPipeServerStream stream ;
198
198
var pipeOptions = NamedPipeOptions . Asynchronous | NamedPipeOptions . WriteThrough ;
199
+ if ( ! _hasFirstPipeStarted )
200
+ {
201
+ // The first server stream created should validate that no one else is listening with a given name.
202
+ // Only the first server stream should make this test. The listener will almost always create multiple streams
203
+ // to listen on multiple threads and to handle parallel requests. The pool policy must be updated that the
204
+ // setting isn't needed after the first stream.
205
+ pipeOptions |= NamedPipeOptions . FirstPipeInstance ;
206
+ }
199
207
if ( _options . CurrentUserOnly )
200
208
{
201
209
pipeOptions |= NamedPipeOptions . CurrentUserOnly ;
@@ -228,5 +236,10 @@ public NamedPipeServerStream Create()
228
236
}
229
237
230
238
public bool Return ( NamedPipeServerStream obj ) => ! obj . IsConnected ;
239
+
240
+ public void SetFirstPipeStarted ( )
241
+ {
242
+ _hasFirstPipeStarted = true ;
243
+ }
231
244
}
232
245
}
0 commit comments