22// The .NET Foundation licenses this file to you under the MIT license.
33// See the LICENSE file in the project root for more information.
44
5+ using System . Collections . Generic ;
56using System . Diagnostics ;
6- using System . Linq ;
77using System . Threading . Tasks ;
88using System . Threading ;
99
1010namespace System . Net . NetworkInformation
1111{
1212 // Linux implementation of NetworkChange
13- public class NetworkChange
13+ public partial class NetworkChange
1414 {
15- private static NetworkAddressChangedEventHandler s_addressChangedSubscribers ;
16- private static NetworkAvailabilityChangedEventHandler s_availabilityChangedSubscribers ;
1715 private static volatile int s_socket = 0 ;
1816 // Lock controlling access to delegate subscriptions, socket initialization, availability-changed state and timer.
1917 private static readonly object s_gate = new object ( ) ;
@@ -37,30 +35,37 @@ public static event NetworkAddressChangedEventHandler NetworkAddressChanged
3735 {
3836 add
3937 {
40- lock ( s_gate )
38+ if ( value != null )
4139 {
42- if ( s_socket == 0 )
40+ lock ( s_gate )
4341 {
44- CreateSocket ( ) ;
45- }
42+ if ( s_socket == 0 )
43+ {
44+ CreateSocket ( ) ;
45+ }
4646
47- s_addressChangedSubscribers += value ;
47+ s_addressChangedSubscribers . TryAdd ( value , ExecutionContext . Capture ( ) ) ;
48+ }
4849 }
4950 }
5051 remove
5152 {
52- lock ( s_gate )
53+ if ( value != null )
5354 {
54- if ( s_addressChangedSubscribers == null && s_availabilityChangedSubscribers == null )
55+ lock ( s_gate )
5556 {
56- Debug . Assert ( s_socket == 0 , "s_socket != 0, but there are no subscribers to NetworkAddressChanged or NetworkAvailabilityChanged." ) ;
57- return ;
58- }
57+ if ( s_addressChangedSubscribers . Count == 0 && s_availabilityChangedSubscribers . Count == 0 )
58+ {
59+ Debug . Assert ( s_socket == 0 ,
60+ "s_socket != 0, but there are no subscribers to NetworkAddressChanged or NetworkAvailabilityChanged." ) ;
61+ return ;
62+ }
5963
60- s_addressChangedSubscribers -= value ;
61- if ( s_addressChangedSubscribers == null && s_availabilityChangedSubscribers == null )
62- {
63- CloseSocket ( ) ;
64+ s_addressChangedSubscribers . Remove ( value ) ;
65+ if ( s_addressChangedSubscribers . Count == 0 && s_availabilityChangedSubscribers . Count == 0 )
66+ {
67+ CloseSocket ( ) ;
68+ }
6469 }
6570 }
6671 }
@@ -70,50 +75,74 @@ public static event NetworkAvailabilityChangedEventHandler NetworkAvailabilityCh
7075 {
7176 add
7277 {
73- lock ( s_gate )
78+ if ( value != null )
7479 {
75- if ( s_socket == 0 )
76- {
77- CreateSocket ( ) ;
78- }
79- if ( s_availabilityTimer == null )
80+ lock ( s_gate )
8081 {
81- s_availabilityTimer = new Timer ( s_availabilityTimerFiredCallback , null , - 1 , - 1 ) ;
82- }
82+ if ( s_socket == 0 )
83+ {
84+ CreateSocket ( ) ;
85+ }
86+
87+ if ( s_availabilityTimer == null )
88+ {
89+ // Don't capture the current ExecutionContext and its AsyncLocals onto the timer causing them to live forever
90+ bool restoreFlow = false ;
91+ try
92+ {
93+ if ( ! ExecutionContext . IsFlowSuppressed ( ) )
94+ {
95+ ExecutionContext . SuppressFlow ( ) ;
96+ restoreFlow = true ;
97+ }
98+
99+ s_availabilityTimer = new Timer ( s_availabilityTimerFiredCallback , null , Timeout . Infinite , Timeout . Infinite ) ;
100+ }
101+ finally
102+ {
103+ // Restore the current ExecutionContext
104+ if ( restoreFlow )
105+ ExecutionContext . RestoreFlow ( ) ;
106+ }
107+ }
83108
84- s_availabilityChangedSubscribers += value ;
109+ s_availabilityChangedSubscribers . TryAdd ( value , ExecutionContext . Capture ( ) ) ;
110+ }
85111 }
86112 }
87113 remove
88114 {
89- lock ( s_gate )
115+ if ( value != null )
90116 {
91- if ( s_addressChangedSubscribers == null && s_availabilityChangedSubscribers == null )
92- {
93- Debug . Assert ( s_socket == 0 , "s_socket != 0, but there are no subscribers to NetworkAddressChanged or NetworkAvailabilityChanged." ) ;
94- return ;
95- }
96-
97- s_availabilityChangedSubscribers -= value ;
98- if ( s_availabilityChangedSubscribers == null )
117+ lock ( s_gate )
99118 {
100- if ( s_availabilityTimer != null )
119+ if ( s_addressChangedSubscribers . Count == 0 && s_availabilityChangedSubscribers . Count == 0 )
101120 {
102- s_availabilityTimer . Dispose ( ) ;
103- s_availabilityTimer = null ;
104- s_availabilityHasChanged = false ;
121+ Debug . Assert ( s_socket == 0 ,
122+ "s_socket != 0, but there are no subscribers to NetworkAddressChanged or NetworkAvailabilityChanged." ) ;
123+ return ;
105124 }
106125
107- if ( s_addressChangedSubscribers == null )
126+ s_availabilityChangedSubscribers . Remove ( value ) ;
127+ if ( s_availabilityChangedSubscribers . Count == 0 )
108128 {
109- CloseSocket ( ) ;
129+ if ( s_availabilityTimer != null )
130+ {
131+ s_availabilityTimer . Dispose ( ) ;
132+ s_availabilityTimer = null ;
133+ s_availabilityHasChanged = false ;
134+ }
135+
136+ if ( s_addressChangedSubscribers . Count == 0 )
137+ {
138+ CloseSocket ( ) ;
139+ }
110140 }
111141 }
112142 }
113143 }
114144 }
115145
116-
117146 private static void CreateSocket ( )
118147 {
119148 Debug . Assert ( s_socket == 0 , "s_socket != 0, must close existing socket before opening another." ) ;
@@ -150,7 +179,7 @@ private static void LoopReadSocket(int socket)
150179 Interop . Sys . ReadEvents ( socket , s_networkChangeCallback ) ;
151180 }
152181 }
153-
182+
154183 private static void ProcessEvent ( int socket , Interop . Sys . NetworkChangeKind kind )
155184 {
156185 if ( kind != Interop . Sys . NetworkChangeKind . None )
@@ -171,7 +200,7 @@ private static void OnSocketEvent(Interop.Sys.NetworkChangeKind kind)
171200 {
172201 case Interop . Sys . NetworkChangeKind . AddressAdded :
173202 case Interop . Sys . NetworkChangeKind . AddressRemoved :
174- s_addressChangedSubscribers ? . Invoke ( null , EventArgs . Empty ) ;
203+ OnAddressChanged ( ) ;
175204 break ;
176205 case Interop . Sys . NetworkChangeKind . AvailabilityChanged :
177206 lock ( s_gate )
@@ -189,18 +218,77 @@ private static void OnSocketEvent(Interop.Sys.NetworkChangeKind kind)
189218 }
190219 }
191220
221+ private static void OnAddressChanged ( )
222+ {
223+ Dictionary < NetworkAddressChangedEventHandler , ExecutionContext > addressChangedSubscribers = null ;
224+
225+ lock ( s_gate )
226+ {
227+ if ( s_addressChangedSubscribers . Count > 0 )
228+ {
229+ addressChangedSubscribers = new Dictionary < NetworkAddressChangedEventHandler , ExecutionContext > ( s_addressChangedSubscribers ) ;
230+ }
231+ }
232+
233+ if ( addressChangedSubscribers != null )
234+ {
235+ foreach ( KeyValuePair < NetworkAddressChangedEventHandler , ExecutionContext >
236+ subscriber in addressChangedSubscribers )
237+ {
238+ NetworkAddressChangedEventHandler handler = subscriber . Key ;
239+ ExecutionContext ec = subscriber . Value ;
240+
241+ if ( ec == null ) // Flow supressed
242+ {
243+ handler ( null , EventArgs . Empty ) ;
244+ }
245+ else
246+ {
247+ ExecutionContext . Run ( ec , s_runAddressChangedHandler , handler ) ;
248+ }
249+ }
250+ }
251+ }
252+
192253 private static void OnAvailabilityTimerFired ( object state )
193254 {
194- bool changed ;
255+ Dictionary < NetworkAvailabilityChangedEventHandler , ExecutionContext > availabilityChangedSubscribers = null ;
256+
195257 lock ( s_gate )
196258 {
197- changed = s_availabilityHasChanged ;
198- s_availabilityHasChanged = false ;
259+ if ( s_availabilityHasChanged )
260+ {
261+ s_availabilityHasChanged = false ;
262+ if ( s_availabilityChangedSubscribers . Count > 0 )
263+ {
264+ availabilityChangedSubscribers =
265+ new Dictionary < NetworkAvailabilityChangedEventHandler , ExecutionContext > (
266+ s_availabilityChangedSubscribers ) ;
267+ }
268+ }
199269 }
200270
201- if ( changed )
271+ if ( availabilityChangedSubscribers != null )
202272 {
203- s_availabilityChangedSubscribers ? . Invoke ( null , new NetworkAvailabilityEventArgs ( NetworkInterface . GetIsNetworkAvailable ( ) ) ) ;
273+ bool isAvailable = NetworkInterface . GetIsNetworkAvailable ( ) ;
274+ NetworkAvailabilityEventArgs args = isAvailable ? s_availableEventArgs : s_notAvailableEventArgs ;
275+ ContextCallback callbackContext = isAvailable ? s_runHandlerAvailable : s_runHandlerNotAvailable ;
276+
277+ foreach ( KeyValuePair < NetworkAvailabilityChangedEventHandler , ExecutionContext >
278+ subscriber in availabilityChangedSubscribers )
279+ {
280+ NetworkAvailabilityChangedEventHandler handler = subscriber . Key ;
281+ ExecutionContext ec = subscriber . Value ;
282+
283+ if ( ec == null ) // Flow supressed
284+ {
285+ handler ( null , args ) ;
286+ }
287+ else
288+ {
289+ ExecutionContext . Run ( ec , callbackContext , handler ) ;
290+ }
291+ }
204292 }
205293 }
206294 }
0 commit comments