PageRenderTime 48ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/src/System.Net.NetworkInformation/src/System/Net/NetworkInformation/NetworkAddressChange.OSX.cs

https://gitlab.com/0072016/0072016-corefx-
C# | 188 lines | 139 code | 29 blank | 20 comment | 19 complexity | ff9f2ffa6e44f0ea87babb7624d7255e MD5 | raw file
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. using Microsoft.Win32.SafeHandles;
  5. using System.Diagnostics;
  6. using System.Threading;
  7. using CFStringRef = System.IntPtr;
  8. using CFRunLoopRef = System.IntPtr;
  9. namespace System.Net.NetworkInformation
  10. {
  11. // OSX implementation of NetworkChange
  12. // See <SystemConfiguration/SystemConfiguration.h> and its documentation, as well as
  13. // the documentation for CFRunLoop for more information on the components involved.
  14. public class NetworkChange
  15. {
  16. private static object s_lockObj = new object();
  17. // The list of current subscribers.
  18. private static NetworkAddressChangedEventHandler s_addressChangedSubscribers;
  19. // The dynamic store. We listen to changes in the IPv4 and IPv6 address keys.
  20. // When those keys change, our callback below is called (OnAddressChanged).
  21. private static SafeCreateHandle s_dynamicStoreRef;
  22. // The callback used when registered keys in the dynamic store change.
  23. private static readonly Interop.SystemConfiguration.SCDynamicStoreCallBack s_storeCallback = OnAddressChanged;
  24. // The RunLoop source, created over the above SCDynamicStore.
  25. private static SafeCreateHandle s_runLoopSource;
  26. // Listener thread that adds the RunLoopSource to its RunLoop.
  27. private static Thread s_runLoopThread;
  28. // The listener thread's CFRunLoop.
  29. private static CFRunLoopRef s_runLoop;
  30. // Use an event to try to prevent StartRaisingEvents from returning before the
  31. // RunLoop actually begins. This will mitigate a race condition where the watcher
  32. // thread hasn't completed initialization and stop is called before the RunLoop has even started.
  33. private static readonly AutoResetEvent s_runLoopStartedEvent = new AutoResetEvent(false);
  34. private static readonly AutoResetEvent s_runLoopEndedEvent = new AutoResetEvent(false);
  35. public static event NetworkAddressChangedEventHandler NetworkAddressChanged
  36. {
  37. add
  38. {
  39. lock (s_lockObj)
  40. {
  41. if (s_addressChangedSubscribers == null)
  42. {
  43. CreateAndStartRunLoop();
  44. }
  45. s_addressChangedSubscribers += value;
  46. }
  47. }
  48. remove
  49. {
  50. lock (s_lockObj)
  51. {
  52. bool hadSubscribers = s_addressChangedSubscribers != null;
  53. s_addressChangedSubscribers -= value;
  54. if (hadSubscribers && s_addressChangedSubscribers == null)
  55. {
  56. StopRunLoop();
  57. }
  58. }
  59. }
  60. }
  61. private static unsafe void CreateAndStartRunLoop()
  62. {
  63. Debug.Assert(s_dynamicStoreRef == null);
  64. var storeContext = new Interop.SystemConfiguration.SCDynamicStoreContext();
  65. using (SafeCreateHandle storeName = Interop.CoreFoundation.CFStringCreateWithCString("NetworkAddressChange.OSX"))
  66. {
  67. s_dynamicStoreRef = Interop.SystemConfiguration.SCDynamicStoreCreate(
  68. storeName.DangerousGetHandle(),
  69. s_storeCallback,
  70. &storeContext);
  71. }
  72. // Notification key string parts. We want to match notification keys
  73. // for any kind of IP address change, addition, or removal.
  74. using (SafeCreateHandle dynamicStoreDomainStateString = Interop.CoreFoundation.CFStringCreateWithCString("State:"))
  75. using (SafeCreateHandle compAnyRegexString = Interop.CoreFoundation.CFStringCreateWithCString("[^/]+"))
  76. using (SafeCreateHandle entNetIpv4String = Interop.CoreFoundation.CFStringCreateWithCString("IPv4"))
  77. using (SafeCreateHandle entNetIpv6String = Interop.CoreFoundation.CFStringCreateWithCString("IPv6"))
  78. {
  79. if (dynamicStoreDomainStateString.IsInvalid || compAnyRegexString.IsInvalid
  80. || entNetIpv4String.IsInvalid || entNetIpv6String.IsInvalid)
  81. {
  82. s_dynamicStoreRef.Dispose();
  83. s_dynamicStoreRef = null;
  84. throw new NetworkInformationException(SR.net_PInvokeError);
  85. }
  86. using (SafeCreateHandle ipv4Pattern = Interop.SystemConfiguration.SCDynamicStoreKeyCreateNetworkServiceEntity(
  87. dynamicStoreDomainStateString.DangerousGetHandle(),
  88. compAnyRegexString.DangerousGetHandle(),
  89. entNetIpv4String.DangerousGetHandle()))
  90. using (SafeCreateHandle ipv6Pattern = Interop.SystemConfiguration.SCDynamicStoreKeyCreateNetworkServiceEntity(
  91. dynamicStoreDomainStateString.DangerousGetHandle(),
  92. compAnyRegexString.DangerousGetHandle(),
  93. entNetIpv6String.DangerousGetHandle()))
  94. using (SafeCreateHandle patterns = Interop.CoreFoundation.CFArrayCreate(
  95. new CFStringRef[2]
  96. {
  97. ipv4Pattern.DangerousGetHandle(),
  98. ipv6Pattern.DangerousGetHandle()
  99. }, 2))
  100. {
  101. // Try to register our pattern strings with the dynamic store instance.
  102. if (patterns.IsInvalid || !Interop.SystemConfiguration.SCDynamicStoreSetNotificationKeys(
  103. s_dynamicStoreRef.DangerousGetHandle(),
  104. IntPtr.Zero,
  105. patterns.DangerousGetHandle()))
  106. {
  107. s_dynamicStoreRef.Dispose();
  108. s_dynamicStoreRef = null;
  109. throw new NetworkInformationException(SR.net_PInvokeError);
  110. }
  111. // Create a "RunLoopSource" that can be added to our listener thread's RunLoop.
  112. s_runLoopSource = Interop.SystemConfiguration.SCDynamicStoreCreateRunLoopSource(
  113. s_dynamicStoreRef.DangerousGetHandle(),
  114. IntPtr.Zero);
  115. }
  116. }
  117. s_runLoopThread = new Thread(RunLoopThreadStart);
  118. s_runLoopThread.Start();
  119. s_runLoopStartedEvent.WaitOne(); // Wait for the new thread to finish initialization.
  120. }
  121. private static void RunLoopThreadStart()
  122. {
  123. Debug.Assert(s_runLoop == IntPtr.Zero);
  124. s_runLoop = Interop.RunLoop.CFRunLoopGetCurrent();
  125. Interop.RunLoop.CFRunLoopAddSource(
  126. s_runLoop,
  127. s_runLoopSource.DangerousGetHandle(),
  128. Interop.RunLoop.kCFRunLoopDefaultMode.DangerousGetHandle());
  129. s_runLoopStartedEvent.Set();
  130. Interop.RunLoop.CFRunLoopRun();
  131. Interop.RunLoop.CFRunLoopRemoveSource(
  132. s_runLoop,
  133. s_runLoopSource.DangerousGetHandle(),
  134. Interop.RunLoop.kCFRunLoopDefaultMode.DangerousGetHandle());
  135. s_runLoop = IntPtr.Zero;
  136. s_runLoopSource.Dispose();
  137. s_runLoopSource = null;
  138. s_dynamicStoreRef.Dispose();
  139. s_dynamicStoreRef = null;
  140. s_runLoopEndedEvent.Set();
  141. }
  142. private static void StopRunLoop()
  143. {
  144. Debug.Assert(s_runLoop != IntPtr.Zero);
  145. Debug.Assert(s_runLoopSource != null);
  146. Debug.Assert(s_dynamicStoreRef != null);
  147. Interop.RunLoop.CFRunLoopStop(s_runLoop);
  148. s_runLoopEndedEvent.WaitOne();
  149. }
  150. private static void OnAddressChanged(IntPtr store, IntPtr changedKeys, IntPtr info)
  151. {
  152. NetworkAddressChangedEventHandler handler = s_addressChangedSubscribers;
  153. if (handler != null)
  154. {
  155. handler(null, EventArgs.Empty);
  156. }
  157. }
  158. }
  159. }