From a4314b3168b1f411f3cf2cab39effb6c16a5a21e Mon Sep 17 00:00:00 2001 From: Edwin Wu Date: Wed, 13 Aug 2025 04:34:52 -0700 Subject: [PATCH] Added a timeout for each operation that block the thread. PiperOrigin-RevId: 794517029 --- .../implementation/apple/ble_medium.mm | 110 +++++++++++++----- 1 file changed, 82 insertions(+), 28 deletions(-) diff --git a/internal/platform/implementation/apple/ble_medium.mm b/internal/platform/implementation/apple/ble_medium.mm index 1c9277b2..4073be42 100644 --- a/internal/platform/implementation/apple/ble_medium.mm +++ b/internal/platform/implementation/apple/ble_medium.mm @@ -54,7 +54,9 @@ #import "internal/platform/implementation/apple/utils.h" static NSString *const kWeaveServiceUUID = @"FEF3"; -static const UInt8 kRequestConnectionTimeoutInSeconds = 12; + +// Timeout for BLE operations that are initiated by a call to a public API. +static const UInt8 kApiTimeoutInSeconds = 12; static NSTimeInterval const kThresholdInterval = 2; // 2 seconds static NSTimeInterval const kAdvertisementPacketsMapExpirationTimeInterval = 600; // 10 minutes @@ -121,11 +123,15 @@ bool BleMedium::StartAdvertising(const api::ble_v2::BleAdvertisementData &advert completionHandler:^(NSError *error) { if (error != nil) { GNCLoggerError(@"Failed to start advertising: %@", error); + blockError = error; } - blockError = error; dispatch_semaphore_signal(semaphore); }]; - dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, kApiTimeoutInSeconds * NSEC_PER_SEC); + if (dispatch_semaphore_wait(semaphore, timeout) != 0) { + GNCLoggerError(@"Start advertising operation timed out."); + return false; + } return blockError == nil; } @@ -137,11 +143,15 @@ bool BleMedium::StopAdvertising() { [medium_ stopAdvertisingWithCompletionHandler:^(NSError *error) { if (error != nil) { GNCLoggerError(@"Failed to stop advertising: %@", error); + blockError = error; } - blockError = error; dispatch_semaphore_signal(semaphore); }]; - dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, kApiTimeoutInSeconds * NSEC_PER_SEC); + if (dispatch_semaphore_wait(semaphore, timeout) != 0) { + GNCLoggerError(@"Stop advertising operation timed out."); + return false; + } return blockError == nil; } @@ -244,11 +254,15 @@ bool BleMedium::StartScanning(const Uuid &service_uuid, api::ble_v2::TxPowerLeve completionHandler:^(NSError *error) { if (error != nil) { GNCLoggerError(@"Failed to start scanning: %@", error); + blockError = error; } - blockError = error; dispatch_semaphore_signal(semaphore); }]; - dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, kApiTimeoutInSeconds * NSEC_PER_SEC); + if (dispatch_semaphore_wait(semaphore, timeout) != 0) { + GNCLoggerError(@"Start scanning operation timed out."); + return false; + } return blockError == nil; } @@ -291,7 +305,11 @@ bool BleMedium::StartMultipleServicesScanning(const std::vector &service_u } dispatch_semaphore_signal(semaphore); }]; - dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, kApiTimeoutInSeconds * NSEC_PER_SEC); + if (dispatch_semaphore_wait(semaphore, timeout) != 0) { + GNCLoggerError(@"Start scanning for multiple services operation timed out."); + return false; + } return blockError == nil; } @@ -303,11 +321,15 @@ bool BleMedium::StopScanning() { [medium_ stopScanningWithCompletionHandler:^(NSError *error) { if (error != nil) { GNCLoggerError(@"Failed to stop scanning: %@", error); + blockError = error; } - blockError = error; dispatch_semaphore_signal(semaphore); }]; - dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, kApiTimeoutInSeconds * NSEC_PER_SEC); + if (dispatch_semaphore_wait(semaphore, timeout) != 0) { + GNCLoggerError(@"Stop scanning operation timed out."); + return false; + } return blockError == nil; } @@ -318,12 +340,16 @@ bool BleMedium::ResumeMediumScanning() { __block NSError *blockError = nil; [medium_ resumeMediumScanning:^(NSError *error) { if (error != nil) { - GNCLoggerError(@"Failed to start scanning for multiple services: %@", error); + GNCLoggerError(@"Failed to resume scanning: %@", error); blockError = error; } dispatch_semaphore_signal(semaphore); }]; - dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, kApiTimeoutInSeconds * NSEC_PER_SEC); + if (dispatch_semaphore_wait(semaphore, timeout) != 0) { + GNCLoggerError(@"ResumeMediumScanning operation timed out."); + return false; + } return blockError == nil; } @@ -332,15 +358,23 @@ std::unique_ptr BleMedium::StartGattServer( api::ble_v2::ServerGattConnectionCallback callback) { dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); __block GNCBLEGATTServer *blockServer = nil; + __block NSError *blockError = nil; [medium_ startGATTServerWithCompletionHandler:^(GNCBLEGATTServer *server, NSError *error) { if (error != nil) { GNCLoggerError(@"Error starting GATT server: %@", error); + blockError = error; + } else { + GNCLoggerInfo(@"Starting GATT server operation completed."); + blockServer = server; } - blockServer = server; dispatch_semaphore_signal(semaphore); }]; - dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); - if (!blockServer) { + dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, kApiTimeoutInSeconds * NSEC_PER_SEC); + if (dispatch_semaphore_wait(semaphore, timeout) != 0) { + GNCLoggerError(@"Starting GATT server operation timed out."); + return nullptr; + } + if (!blockServer || blockError != nil) { return nullptr; } return std::make_unique(blockServer); @@ -378,6 +412,7 @@ std::unique_ptr BleMedium::ConnectToGattServer( dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); __block GNCBLEGATTClient *blockClient = nil; + __block NSError *blockError = nil; [medium_ connectToGATTServerForPeripheral:peripheral disconnectionHandler:^(void) { blockCallback.disconnected_cb(); @@ -385,12 +420,19 @@ std::unique_ptr BleMedium::ConnectToGattServer( completionHandler:^(GNCBLEGATTClient *client, NSError *error) { if (error != nil) { GNCLoggerError(@"Error connecting to GATT server: %@", error); + blockError = error; + } else { + GNCLoggerInfo(@"Connecting to GATT server operation completed."); + blockClient = client; } - blockClient = client; dispatch_semaphore_signal(semaphore); }]; - dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); - if (!blockClient) { + dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, kApiTimeoutInSeconds * NSEC_PER_SEC); + if (dispatch_semaphore_wait(semaphore, timeout) != 0) { + GNCLoggerError(@"Connecting to GATT server operation timed out."); + return nullptr; + } + if (!blockClient || blockError != nil) { return nullptr; } return std::make_unique(blockClient); @@ -431,12 +473,16 @@ std::unique_ptr BleMedium::OpenServerSocket( bleServiceAddedCompletion:^(NSError *error) { if (error != nil) { GNCLoggerError(@"Failed to add Weave service: %@", error); + blockError = error; } - blockError = error; dispatch_semaphore_signal(semaphore); }]; [socketPeripheralManager_ start]; - dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, kApiTimeoutInSeconds * NSEC_PER_SEC); + if (dispatch_semaphore_wait(semaphore, timeout) != 0) { + GNCLoggerError(@"OpenServerSocket operation timed out."); + return nullptr; + } if (blockError != nil) { return nullptr; } @@ -492,13 +538,18 @@ std::unique_ptr BleMedium::OpenL2capServerSoc } } peripheralManager:nil]; - dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, kApiTimeoutInSeconds * NSEC_PER_SEC); + if (dispatch_semaphore_wait(semaphore, timeout) != 0) { + GNCLoggerError(@"OpenL2capServerSocket operation timed out."); + return nullptr; + } if (blockPSMPublishedError != nil) { return nullptr; } return std::move(l2cap_server_socket); } + // TODO(b/290385712): Add support for @c cancellation_flag. // TODO(b/293336684): Old Weave code that need to be deleted once shared Weave is complete. std::unique_ptr BleMedium::Connect( @@ -506,7 +557,7 @@ std::unique_ptr BleMedium::Connect( api::ble_v2::BlePeripheral::UniqueId peripheral_id, CancellationFlag *cancellation_flag) { id peripheral = peripherals_.Get(peripheral_id); if (!peripheral) { - GNCLoggerError(@"[NEARBY] Failed to connect to Gatt server: peripheral is not found."); + GNCLoggerError(@"Failed to connect to Gatt server: peripheral is not found."); return nullptr; } @@ -542,7 +593,11 @@ std::unique_ptr BleMedium::Connect( dispatch_semaphore_signal(semaphore); }); }]; - dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, kApiTimeoutInSeconds * NSEC_PER_SEC); + if (dispatch_semaphore_wait(semaphore, timeout) != 0) { + GNCLoggerError(@"Failed to connect BLE socket: timeout."); + return nullptr; + } if (socket == nullptr) { return nullptr; } @@ -557,12 +612,10 @@ std::unique_ptr BleMedium::ConnectOverL2cap( api::ble_v2::BlePeripheral::UniqueId peripheral_id, CancellationFlag *cancellation_flag) { id peripheral = peripherals_.Get(peripheral_id); if (!peripheral) { - GNCLoggerError(@"[NEARBY] Failed to connect over L2CAP: peripheral is not found."); + GNCLoggerError(@"Failed to connect over L2CAP: peripheral is not found."); return nullptr; } dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); - dispatch_time_t timeout = - dispatch_time(DISPATCH_TIME_NOW, kRequestConnectionTimeoutInSeconds * NSEC_PER_SEC); __block std::unique_ptr socket; __block NSError *openError = nil; const std::string &service_id_str = service_id; @@ -591,13 +644,14 @@ std::unique_ptr BleMedium::ConnectOverL2cap( dispatch_semaphore_signal(semaphore); }]; }]; + dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, kApiTimeoutInSeconds * NSEC_PER_SEC); if (dispatch_semaphore_wait(semaphore, timeout) != 0) { - GNCLoggerError(@"[NEARBY] Failed to connect over L2CAP: timeout."); + GNCLoggerError(@"Failed to connect over L2CAP: timeout."); return nullptr; } if (socket == nullptr) { if (openError != nil) { - GNCLoggerError(@"[NEARBY] Failed to connect over L2CAP:%@.", openError); + GNCLoggerError(@"Failed to connect over L2CAP:%@.", openError); } return nullptr; }