From 0b51a5bbbd57c0710889ac3928b4210ecd2fff71 Mon Sep 17 00:00:00 2001 From: Foti Dim Date: Tue, 18 Mar 2025 11:11:28 +0100 Subject: [PATCH] Fix error on Windows scan (#146) * Error on windows scan * Implement thread safe cache handling * Fix start scan method * Update windows/src/universal_ble_thread_safe.h Co-authored-by: Foti Dim --------- Co-authored-by: Rohit Sangwan --- windows/CMakeLists.txt | 1 + windows/src/universal_ble_plugin.cpp | 39 +++++++++---------- windows/src/universal_ble_plugin.h | 6 ++- windows/src/universal_ble_thread_safe.h | 52 +++++++++++++++++++++++++ 4 files changed, 76 insertions(+), 22 deletions(-) create mode 100644 windows/src/universal_ble_thread_safe.h diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index b924871..8826385 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -44,6 +44,7 @@ list(APPEND PLUGIN_SOURCES "src/pin_entry.h" "src/universal_ble_filter_util.cpp" "src/universal_ble_filter_util.h" + "src/universal_ble_thread_safe.h" ) add_library(${PLUGIN_NAME} SHARED diff --git a/windows/src/universal_ble_plugin.cpp b/windows/src/universal_ble_plugin.cpp index 525e415..34f6130 100644 --- a/windows/src/universal_ble_plugin.cpp +++ b/windows/src/universal_ble_plugin.cpp @@ -667,10 +667,10 @@ namespace universal_ble // if device is already discovered in deviceWatcher then merge the scan result void UniversalBlePlugin::pushUniversalScanResult(UniversalBleScanResult scanResult, bool isConnectable) { - auto it = scanResults.find(scanResult.device_id()); - if (it != scanResults.end()) + std::optional it = scanResults.get(scanResult.device_id()); + if (it.has_value()) { - UniversalBleScanResult ¤tScanResult = it->second; + UniversalBleScanResult ¤tScanResult = it.value(); bool shouldUpdate = false; // Check if current scanResult name is longer than the received scanResult name @@ -708,16 +708,13 @@ namespace universal_ble // if nothing to update then return if (!shouldUpdate) + { return; + } + } - // update the current scan result - currentScanResult = scanResult; - } - else - { - // if not present, insert the new scan result - scanResults.insert(std::make_pair(scanResult.device_id(), scanResult)); - } + // Update cache + scanResults.insert_or_assign(scanResult.device_id(), scanResult); // Filter final result before sending to Flutter if (isConnectable && filterDevice(scanResult)) @@ -757,11 +754,13 @@ namespace universal_ble deviceWatcherUpdatedToken = deviceWatcher.Updated([this](DeviceWatcher sender, DeviceInformationUpdate deviceInfoUpdate) { std::string deviceId = winrt::to_string(deviceInfoUpdate.Id()); - auto it = deviceWatcherDevices.find(deviceId); - if (it != deviceWatcherDevices.end()) + auto it = deviceWatcherDevices.get(deviceId); + if (it.has_value()) { - it->second.Update(deviceInfoUpdate); - onDeviceInfoReceived(it->second); + auto value = it.value(); + value.Update(deviceInfoUpdate); + deviceWatcherDevices.insert_or_assign(deviceId, value); + onDeviceInfoReceived(value); } // On Device Updated }); @@ -769,7 +768,7 @@ namespace universal_ble deviceWatcherRemovedToken = deviceWatcher.Removed([this](DeviceWatcher sender, DeviceInformationUpdate args) { std::string deviceId = winrt::to_string(args.Id()); - deviceWatcherDevices.erase(deviceId); + deviceWatcherDevices.remove(deviceId); // On Device Removed }); @@ -820,7 +819,7 @@ namespace universal_ble std::string deviceAddress = winrt::to_string(bluetoothAddressPropertyValue.GetString()); // Update device info if already discovered in advertisementWatcher - if (scanResults.count(deviceAddress) > 0) + if (scanResults.get(deviceAddress).has_value()) { bool isPaired = deviceInfo.Pairing().IsPaired(); if (properties.HasKey(isPairedKey)) @@ -900,10 +899,10 @@ namespace universal_ble universalScanResult.set_services(services); // check if this device already discovered in deviceWatcher - auto it = deviceWatcherDevices.find(deviceId); - if (it != deviceWatcherDevices.end()) + auto it = deviceWatcherDevices.get(deviceId); + if (it.has_value()) { - auto &deviceInfo = it->second; + auto &deviceInfo = it.value(); auto properties = deviceInfo.Properties(); // Update Paired Status diff --git a/windows/src/universal_ble_plugin.h b/windows/src/universal_ble_plugin.h index 8cac659..52891ad 100644 --- a/windows/src/universal_ble_plugin.h +++ b/windows/src/universal_ble_plugin.h @@ -21,6 +21,7 @@ #include "helper/universal_ble_base.h" #include "generated/universal_ble.g.h" #include "ui_thread_handler.hpp" +#include "universal_ble_thread_safe.h" namespace universal_ble { @@ -92,8 +93,10 @@ namespace universal_ble RadioState oldRadioState = RadioState::Unknown; BluetoothLEAdvertisementWatcher bluetoothLEWatcher{nullptr}; DeviceWatcher deviceWatcher{nullptr}; + std::unordered_map> connectedDevices{}; - std::unordered_map deviceWatcherDevices{}; + ThreadSafeMap deviceWatcherDevices{}; + ThreadSafeMap scanResults{}; winrt::event_token bluetoothLEWatcherReceivedToken; winrt::event_token deviceWatcherAddedToken; @@ -112,7 +115,6 @@ namespace universal_ble void onDeviceInfoReceived(DeviceInformation deviceInfo); std::string GattCommunicationStatusToString(GattCommunicationStatus status); - std::unordered_map scanResults{}; winrt::event_revoker radioStateChangedRevoker; winrt::fire_and_forget ConnectAsync(uint64_t bluetoothAddress); void BluetoothLEDevice_ConnectionStatusChanged(BluetoothLEDevice sender, IInspectable args); diff --git a/windows/src/universal_ble_thread_safe.h b/windows/src/universal_ble_thread_safe.h new file mode 100644 index 0000000..e38a0cf --- /dev/null +++ b/windows/src/universal_ble_thread_safe.h @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include +#include + +namespace universal_ble +{ + // Thread Safe Map wrapper + // Use only if you don't care about value ownership and it's OK to work with copies of the data. + template + class ThreadSafeMap + { + private: + std::unordered_map data; + mutable std::shared_mutex mutex; + + public: + void insert_or_assign(const Key &key, const Value &value) + { + std::unique_lock lock(mutex); + data.insert_or_assign(key, value); + } + + bool remove(const Key &key) + { + std::unique_lock lock(mutex); + return data.erase(key) > 0; + } + + std::optional get(const Key &key) const + { + std::shared_lock lock(mutex); + auto it = data.find(key); + return (it != data.end()) ? std::optional(it->second) : std::nullopt; + } + + void clear() + { + std::unique_lock lock(mutex); + data.clear(); + } + + std::map get_snapshot() const + { + std::shared_lock lock(mutex); + return data; + } + }; + +} // namespace universal_ble