mirokuratczyk 3 лет назад
Родитель
Сommit
2c724295fd

+ 15 - 6
MobileLibrary/iOS/PsiphonTunnel/PsiphonTunnel/Network/Reachability/DefaultRouteMonitor.m

@@ -130,21 +130,30 @@ NetworkReachability nw_interface_type_network_reachability(nw_interface_type_t i
         self->monitor = nw_path_monitor_create();
 
         nw_path_monitor_set_queue(self->monitor, self->nwPathMonitorQueue);
-        __block dispatch_group_t group = dispatch_group_create();
-        dispatch_group_enter(group);
+        __block dispatch_semaphore_t sem = dispatch_semaphore_create(0);
 
         nw_path_monitor_set_update_handler(self->monitor, ^(nw_path_t  _Nonnull path) {
             [self pathUpdateHandler:path];
-            if (group != NULL) {
-                dispatch_group_leave(group);
-                group = NULL;
+            if (sem != NULL) {
+                dispatch_semaphore_signal(sem);
+                @synchronized (self) {
+                    // Release memory after `start` has completed. Otherwise we may set `sem` to
+                    // NULL before dispatch_semaphore_wait is called in the enclosing scope and the
+                    // program will crash.
+                    sem = NULL;
+                }
             }
         });
         nw_path_monitor_start(self->monitor);
 
         // Wait for the current path to be emitted before returning to ensure this instance is
         // populated with the current network state. PsiphonTunnel depends on this guarantee.
-        dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
+        // NOTE: This null guard defends against nw_path_monitor_start calling the update handler
+        // synchronously before returning, e.g. with dispatch_sync, which will set `sem` to NULL
+        // because @synchronized provides a reentrant thread level locking mechanism.
+        if (sem != NULL) {
+            dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
+        }
     }
 }