66using System . Diagnostics . Tracing ;
77using System . IO ;
88using System . Linq ;
9+ using System . Net . Sockets ;
10+ using System . Net ;
911using System . Reflection ;
1012using System . Threading . Tasks ;
1113using System . Threading . Tests ;
@@ -1160,6 +1162,95 @@ public void ThreadPoolMinMaxThreadsEventTest()
11601162 } ) . Dispose ( ) ;
11611163 }
11621164
1165+ private sealed class RuntimeEventListener : EventListener
1166+ {
1167+ private const string ClrProviderName = "Microsoft-Windows-DotNETRuntime" ;
1168+ private const EventKeywords ThreadingKeyword = ( EventKeywords ) 0x10000 ;
1169+
1170+ public volatile int tpIOEnqueue = 0 ;
1171+ public volatile int tpIODequeue = 0 ;
1172+ public ManualResetEvent tpWaitIOEnqueueEvent = new ManualResetEvent ( false ) ;
1173+ public ManualResetEvent tpWaitIODequeueEvent = new ManualResetEvent ( false ) ;
1174+
1175+ protected override void OnEventSourceCreated ( EventSource eventSource )
1176+ {
1177+ if ( eventSource . Name . Equals ( ClrProviderName ) )
1178+ {
1179+ EnableEvents ( eventSource , EventLevel . Verbose , ThreadingKeyword ) ;
1180+ }
1181+
1182+ base . OnEventSourceCreated ( eventSource ) ;
1183+ }
1184+
1185+ protected override void OnEventWritten ( EventWrittenEventArgs eventData )
1186+ {
1187+ if ( eventData . EventName . Equals ( "ThreadPoolIOEnqueue" ) )
1188+ {
1189+ Interlocked . Increment ( ref tpIOEnqueue ) ;
1190+ tpWaitIOEnqueueEvent . Set ( ) ;
1191+ }
1192+ else if ( eventData . EventName . Equals ( "ThreadPoolIODequeue" ) )
1193+ {
1194+ Interlocked . Increment ( ref tpIODequeue ) ;
1195+ tpWaitIODequeueEvent . Set ( ) ;
1196+ }
1197+ }
1198+ }
1199+
1200+ [ ConditionalFact ( nameof ( IsThreadingAndRemoteExecutorSupported ) , nameof ( UseWindowsThreadPool ) ) ]
1201+ public void ReadWriteAsyncTest ( )
1202+ {
1203+ RemoteExecutor . Invoke ( async ( ) =>
1204+ {
1205+ using ( RuntimeEventListener eventListener = new RuntimeEventListener ( ) )
1206+ {
1207+ TaskCompletionSource < int > portTcs = new TaskCompletionSource < int > ( ) ;
1208+ TaskCompletionSource < bool > readAsyncReadyTcs = new TaskCompletionSource < bool > ( ) ;
1209+
1210+ async Task StartListenerAsync ( )
1211+ {
1212+ using TcpListener listener = new TcpListener ( IPAddress . Loopback , 0 ) ;
1213+ listener . Start ( ) ;
1214+ int port = ( ( IPEndPoint ) listener . LocalEndpoint ) . Port ;
1215+ portTcs . SetResult ( port ) ;
1216+ using TcpClient client = await listener . AcceptTcpClientAsync ( ) ;
1217+ using ( NetworkStream stream = client . GetStream ( ) )
1218+ {
1219+ byte [ ] buffer = new byte [ 1 ] ;
1220+ Task readAsyncTask = stream . ReadAsync ( buffer , 0 , buffer . Length ) ;
1221+ readAsyncReadyTcs . SetResult ( true ) ;
1222+ await readAsyncTask ;
1223+ }
1224+ listener . Stop ( ) ;
1225+ }
1226+
1227+ async Task StartClientAsync ( )
1228+ {
1229+ int port = await portTcs . Task ;
1230+ using ( TcpClient client = new TcpClient ( new IPEndPoint ( IPAddress . Loopback , 0 ) ) )
1231+ {
1232+ await client . ConnectAsync ( IPAddress . Loopback , port ) ;
1233+ using ( NetworkStream stream = client . GetStream ( ) )
1234+ {
1235+ bool readAsyncReady = await readAsyncReadyTcs . Task ;
1236+ byte [ ] data = new byte [ 1 ] ;
1237+ await stream . WriteAsync ( data , 0 , data . Length ) ;
1238+ }
1239+ }
1240+ }
1241+
1242+ Task listenerTask = StartListenerAsync ( ) ;
1243+ Task clientTask = StartClientAsync ( ) ;
1244+ await Task . WhenAll ( listenerTask , clientTask ) ;
1245+ ManualResetEvent [ ] waitEvents = [ eventListener . tpWaitIOEnqueueEvent , eventListener . tpWaitIODequeueEvent ] ;
1246+
1247+ Assert . True ( WaitHandle . WaitAll ( waitEvents , TimeSpan . FromSeconds ( 15 ) ) ) ; // Assert that there wasn't a timeout
1248+ Assert . True ( eventListener . tpIOEnqueue > 0 ) ;
1249+ Assert . True ( eventListener . tpIODequeue > 0 ) ;
1250+ }
1251+ } ) . Dispose ( ) ;
1252+ }
1253+
11631254 public static bool IsThreadingAndRemoteExecutorSupported =>
11641255 PlatformDetection . IsThreadingSupported && RemoteExecutor . IsSupported ;
11651256
@@ -1169,6 +1260,7 @@ private static bool GetUseWindowsThreadPool()
11691260 return useWindowsThreadPool ;
11701261 }
11711262
1172- private static bool UsePortableThreadPool { get ; } = ! GetUseWindowsThreadPool ( ) ;
1263+ private static bool UseWindowsThreadPool { get ; } = GetUseWindowsThreadPool ( ) ;
1264+ private static bool UsePortableThreadPool { get ; } = ! UseWindowsThreadPool ;
11731265 }
11741266}
0 commit comments