From e895f3c100812f15a1a5670991aad350c8bc539c Mon Sep 17 00:00:00 2001 From: jfcarroll Date: Wed, 1 Sep 2021 12:53:36 -0700 Subject: [PATCH] Initial implementation of ConditionVariable and Mutex PiperOrigin-RevId: 394293910 --- cpp/platform/impl/windows/BUILD | 8 ++ .../impl/windows/bluetooth_classic_medium.cc | 16 ++- .../impl/windows/bluetooth_classic_medium.h | 3 - .../windows/bluetooth_classic_medium_test.cc | 127 +++++++++++++++--- .../impl/windows/condition_variable.cc | 70 ++++++++++ .../impl/windows/condition_variable.h | 28 +++- .../impl/windows/condition_variable_test.cc | 104 ++++++++++++++ cpp/platform/impl/windows/mutex.cc | 90 +++++++++++++ cpp/platform/impl/windows/mutex.h | 35 ++++- cpp/platform/impl/windows/mutex_test.cc | 109 +++++++++++++++ cpp/platform/impl/windows/platform.cc | 4 +- 11 files changed, 554 insertions(+), 40 deletions(-) create mode 100644 cpp/platform/impl/windows/condition_variable.cc create mode 100644 cpp/platform/impl/windows/condition_variable_test.cc create mode 100644 cpp/platform/impl/windows/mutex.cc create mode 100644 cpp/platform/impl/windows/mutex_test.cc diff --git a/cpp/platform/impl/windows/BUILD b/cpp/platform/impl/windows/BUILD index a3410d27..e47be8b5 100644 --- a/cpp/platform/impl/windows/BUILD +++ b/cpp/platform/impl/windows/BUILD @@ -56,6 +56,8 @@ cc_library( "bluetooth_classic_medium.h", "bluetooth_classic_server_socket.h", "bluetooth_classic_socket.h", + "condition_variable.h", + "mutex.h", "server_sync.h", "webrtc.h", "wifi.h", @@ -93,6 +95,8 @@ cc_library( "bluetooth_classic_medium.cc", "bluetooth_classic_server_socket.cc", "bluetooth_classic_socket.cc", + "condition_variable.cc", + "mutex.cc", "platform.cc", "utils.cc", ], @@ -102,6 +106,8 @@ cc_library( "bluetooth_classic_medium.h", "bluetooth_classic_server_socket.h", "bluetooth_classic_socket.h", + "condition_variable.h", + "mutex.h", ], compatible_with = ["//buildenv/target:non_prod"], copts = ["-Ithird_party/nearby_connections/cpp/platform/impl/windows/generated"], @@ -143,8 +149,10 @@ cc_test( srcs = [ "atomic_boolean_test.cc", "atomic_reference_test.cc", + "condition_variable_test.cc", "crypto_test.cc", "input_file_test.cc", + "mutex_test.cc", "output_file_test.cc", ], copts = ["-Iplatform/impl/windows/generated"], diff --git a/cpp/platform/impl/windows/bluetooth_classic_medium.cc b/cpp/platform/impl/windows/bluetooth_classic_medium.cc index e78c93a5..b29e2c64 100644 --- a/cpp/platform/impl/windows/bluetooth_classic_medium.cc +++ b/cpp/platform/impl/windows/bluetooth_classic_medium.cc @@ -14,8 +14,12 @@ #include "platform/impl/windows/bluetooth_classic_medium.h" +#include +#include + #include #include +#include // NOLINT #include #include "platform/base/cancellation_flag.h" @@ -116,12 +120,22 @@ std::unique_ptr BluetoothClassicMedium::ConnectToService( return nullptr; } - if (cancellation_flag == nullptr) { + const std::regex pattern( + "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]" + "{12}$"); + + // Must check for valid pattern as the guid constructor will throw on an + // invalid format + if (!regex_match(service_uuid, pattern)) { return nullptr; } winrt::guid service(service_uuid); + if (cancellation_flag == nullptr) { + return nullptr; + } + BluetoothDevice* currentDevice = dynamic_cast(&remote_device); diff --git a/cpp/platform/impl/windows/bluetooth_classic_medium.h b/cpp/platform/impl/windows/bluetooth_classic_medium.h index b5c0a5b5..a00d2d24 100644 --- a/cpp/platform/impl/windows/bluetooth_classic_medium.h +++ b/cpp/platform/impl/windows/bluetooth_classic_medium.h @@ -15,9 +15,6 @@ #ifndef PLATFORM_IMPL_WINDOWS_BLUETOOTH_CLASSIC_MEDIUM_H_ #define PLATFORM_IMPL_WINDOWS_BLUETOOTH_CLASSIC_MEDIUM_H_ -#include -#include - #include "platform/api/bluetooth_classic.h" #include "platform/impl/windows/bluetooth_classic_device.h" #include "platform/impl/windows/bluetooth_classic_server_socket.h" diff --git a/cpp/platform/impl/windows/bluetooth_classic_medium_test.cc b/cpp/platform/impl/windows/bluetooth_classic_medium_test.cc index 890015c1..06bf3c48 100644 --- a/cpp/platform/impl/windows/bluetooth_classic_medium_test.cc +++ b/cpp/platform/impl/windows/bluetooth_classic_medium_test.cc @@ -14,54 +14,68 @@ #include "platform/impl/windows/bluetooth_classic_medium.h" -#include +#include #include #include -#include "gtest/gtest.h" #include "absl/strings/str_format.h" #include "platform/api/bluetooth_adapter.h" +#include "platform/api/bluetooth_classic.h" #include "platform/impl/windows/bluetooth_adapter.h" #include "platform/impl/windows/bluetooth_classic_device.h" #include "platform/impl/windows/generated/winrt/Windows.Devices.Bluetooth.Rfcomm.h" +#include "gtest/gtest.h" + // TODO(jfcarroll): Find a way to mock winrt components in order to properly // unit test this. Once that's done, unit tests can be written, in a later C/L. // // CAUTION: THIS IS NOT A REAL TEST, THIS EXERCISES THE SCANNER, IT DOES NOT // STOP AND IS INTENDED SOLELY FOR DEBUG AND DEMONSTRATION PURPOSES. DO NOT // ATTEMPT TO BUILD AND RUN THIS TEST ON GOOGLE3 YOU HAVE BEEN WARNED -static int callcount = 0; -static std::map - deviceList; -void device_discovered_cb(location::nearby::api::BluetoothDevice& device) { - const std::string address = device.GetMacAddress(); +using location::nearby::windows::BluetoothDevice; - std::map::const_iterator - it = deviceList.find(address); +typedef std::map + DeviceMap; - std::string buffer = - absl::StrFormat("processing device %s : ", address.c_str()); +class BluetoothClassicMediumTests : public testing::Test { + protected: + static void device_discovered_cb( + location::nearby::api::BluetoothDevice& device) { + const std::string address = device.GetMacAddress(); - OutputDebugStringA(buffer.c_str()); + std::map::const_iterator it = + deviceList.find(address); - if (it == deviceList.end()) { - buffer = absl::StrFormat("adding device %s\n", address.c_str()); + std::string buffer = + absl::StrFormat("processing device %s : ", address.c_str()); OutputDebugStringA(buffer.c_str()); - deviceList[device.GetMacAddress()] = &device; + + if (it == deviceList.end()) { + buffer = absl::StrFormat("adding device %s\n", address.c_str()); + + OutputDebugStringA(buffer.c_str()); + deviceList[device.GetMacAddress()] = &device; + } } -} -void device_name_changed_cb(location::nearby::api::BluetoothDevice& device) {} + static void device_name_changed_cb( + location::nearby::api::BluetoothDevice& device) {} -void device_lost_cb(location::nearby::api::BluetoothDevice& device) { - deviceList.erase(device.GetMacAddress()); -} + static void device_lost_cb(location::nearby::api::BluetoothDevice& device) { + deviceList.erase(device.GetMacAddress()); + } -TEST(TestCaseName, TestName) { + int callcount = 0; + static inline DeviceMap deviceList = {}; +}; + +#ifdef TESTING_LOCALLY +TEST_F(BluetoothClassicMediumTests, ManualTest) { auto bluetoothAdapter = location::nearby::windows::BluetoothAdapter(); std::string bluetoothAdapterName = bluetoothAdapter.GetName(); @@ -97,3 +111,74 @@ TEST(TestCaseName, TestName) { EXPECT_EQ(1, 1); EXPECT_TRUE(true); } +#endif +TEST_F(BluetoothClassicMediumTests, ConnectToServiceNullUuid) { + // Arrange + auto bluetoothAdapter = location::nearby::windows::BluetoothAdapter(); + + std::unique_ptr + bluetoothClassicMedium = + std::make_unique( + bluetoothAdapter); + + location::nearby::windows::BluetoothDevice bluetoothDevice = + location::nearby::windows::BluetoothDevice( + std::string("1D:EA:DB:EE:B9:00")); + location::nearby::CancellationFlag cancellationFlag; + + auto bluetoothClassicMediumImpl = bluetoothClassicMedium.get(); + + // Act + auto asyncResult = bluetoothClassicMediumImpl->ConnectToService( + bluetoothDevice, std::string(), &cancellationFlag); + + // Assert + EXPECT_TRUE(asyncResult.get() == nullptr); +} + +TEST_F(BluetoothClassicMediumTests, ConnectToServiceNullCancellationFlag) { + // Arrange + auto bluetoothAdapter = location::nearby::windows::BluetoothAdapter(); + + std::unique_ptr + bluetoothClassicMedium = + std::make_unique( + bluetoothAdapter); + + location::nearby::windows::BluetoothDevice bluetoothDevice = + location::nearby::windows::BluetoothDevice( + std::string("1D:EA:DB:EE:B9:00")); + + auto bluetoothClassicMediumImpl = bluetoothClassicMedium.get(); + + // Act + auto asyncResult = bluetoothClassicMediumImpl->ConnectToService( + bluetoothDevice, std::string("test service"), nullptr); + + // Assert + EXPECT_TRUE(asyncResult.get() == nullptr); +} + +TEST_F(BluetoothClassicMediumTests, ConnectToServiceWithInvalidServiceUuid) { + // Arrange + auto bluetoothAdapter = location::nearby::windows::BluetoothAdapter(); + + std::unique_ptr + bluetoothClassicMedium = + std::make_unique( + bluetoothAdapter); + + location::nearby::windows::BluetoothDevice bluetoothDevice = + location::nearby::windows::BluetoothDevice( + std::string("1D:EA:DB:EE:B9:00")); + location::nearby::CancellationFlag cancellationFlag; + + auto bluetoothClassicMediumImpl = bluetoothClassicMedium.get(); + + // Act + auto asyncResult = bluetoothClassicMediumImpl->ConnectToService( + bluetoothDevice, std::string("test service"), &cancellationFlag); + + // Assert + EXPECT_TRUE(asyncResult.get() == nullptr); +} diff --git a/cpp/platform/impl/windows/condition_variable.cc b/cpp/platform/impl/windows/condition_variable.cc new file mode 100644 index 00000000..12df2a4d --- /dev/null +++ b/cpp/platform/impl/windows/condition_variable.cc @@ -0,0 +1,70 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "platform/impl/windows/condition_variable.h" + +namespace location { +namespace nearby { +namespace windows { + +// The ConditionVariable class is a synchronization primitive that can be used +// to block a thread, or multiple threads at the same time, until another thread +// both modifies a shared variable (the condition), and notifies the +// ConditionVariable. + +ConditionVariable::ConditionVariable(api::Mutex* mutex) + : mutex_(dynamic_cast(*mutex)) { + InitializeCriticalSection(&critical_section_); +} + +// Notifies all the waiters that condition state has changed. +// TODO(b/184975123): replace with real implementation. +void ConditionVariable::Notify() { + EnterCriticalSection(&critical_section_); + + condition_variable_actual_.notify_all(); + + LeaveCriticalSection(&critical_section_); +} + +// Waits indefinitely for Notify to be called. +// May return prematurely in case of interrupt, if supported by platform. +// Returns kSuccess, or kInterrupted on interrupt. +Exception ConditionVariable::Wait() { + std::unique_lock lock = + std::unique_lock(mutex_.GetWindowsMutex()); + condition_variable_actual_.wait(lock); + + return Exception{Exception::kSuccess}; +} + +// Waits while timeout has not expired for Notify to be called. +// May return prematurely in case of interrupt, if supported by platform. +// Returns kSuccess, or kInterrupted on interrupt. +Exception ConditionVariable::Wait(absl::Duration timeout) { + auto now = std::chrono::system_clock::now(); + std::unique_lock lock = + std::unique_lock(mutex_.GetWindowsMutex()); + if (condition_variable_actual_.wait_until( + lock, now + absl::ToChronoMilliseconds(timeout)) == + std::cv_status::timeout) { + return Exception{Exception::kInterrupted}; + } else { + return Exception{Exception::kSuccess}; + } +} + +} // namespace windows +} // namespace nearby +} // namespace location diff --git a/cpp/platform/impl/windows/condition_variable.h b/cpp/platform/impl/windows/condition_variable.h index cd8c75d4..92a7f4a3 100644 --- a/cpp/platform/impl/windows/condition_variable.h +++ b/cpp/platform/impl/windows/condition_variable.h @@ -15,7 +15,14 @@ #ifndef PLATFORM_IMPL_WINDOWS_CONDITION_VARIABLE_H_ #define PLATFORM_IMPL_WINDOWS_CONDITION_VARIABLE_H_ +#include +#include +#include + +#include // NOLINT + #include "platform/api/condition_variable.h" +#include "platform/impl/windows/mutex.h" namespace location { namespace nearby { @@ -27,24 +34,31 @@ namespace windows { // ConditionVariable. class ConditionVariable : public api::ConditionVariable { public: - // TODO(b/184975123): replace with real implementation. + ConditionVariable(api::Mutex* mutex); + ~ConditionVariable() override = default; // Notifies all the waiters that condition state has changed. - // TODO(b/184975123): replace with real implementation. - void Notify() override {} + void Notify() override; // Waits indefinitely for Notify to be called. // May return prematurely in case of interrupt, if supported by platform. // Returns kSuccess, or kInterrupted on interrupt. - // TODO(b/184975123): replace with real implementation. - Exception Wait() override { return Exception{}; } + Exception Wait() override; // Waits while timeout has not expired for Notify to be called. // May return prematurely in case of interrupt, if supported by platform. // Returns kSuccess, or kInterrupted on interrupt. - // TODO(b/184975123): replace with real implementation. - Exception Wait(absl::Duration timeout) override { return Exception{}; } + Exception Wait(absl::Duration timeout) override; + + private: + location::nearby::windows::Mutex& mutex_; + + std::condition_variable condition_variable_actual_; + std::condition_variable& condition_variable_ = condition_variable_actual_; + + std::unique_lock lock; + CRITICAL_SECTION critical_section_; }; } // namespace windows diff --git a/cpp/platform/impl/windows/condition_variable_test.cc b/cpp/platform/impl/windows/condition_variable_test.cc new file mode 100644 index 00000000..e0a2a4c9 --- /dev/null +++ b/cpp/platform/impl/windows/condition_variable_test.cc @@ -0,0 +1,104 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "platform/impl/windows/condition_variable.h" + +#include // NOLINT + +#include "absl/time/clock.h" +#include "platform/base/exception.h" +#include "platform/impl/windows/mutex.h" + +#include "gtest/gtest.h" + +class ConditionVariableTests : public testing::Test { + public: + class ConditionVariableTest { + public: + ConditionVariableTest() {} + + std::future WaitForEvent(bool timedWait, // NOLINT + const absl::Duration* timeout) { + return std::async( + std::launch::async, + [this, timedWait, timeout]() mutable { + std::thread::id currentThread = std::this_thread::get_id(); + + if (timedWait == true) { + auto result = this->condition_variable_actual_.Wait(*timeout); + if (result.value == location::nearby::Exception::kSuccess) { + return true; + } else { + return false; + } + } else { + this->condition_variable_actual_.Wait(); + } + return true; + }); + } + + void PostEvent() { + std::lock_guard guard(mutex_actual_.GetWindowsMutex()); + condition_variable_actual_.Notify(); + } + + private: + location::nearby::windows::Mutex mutex_actual_ = + location::nearby::windows::Mutex( + location::nearby::windows::Mutex::Mode::kRegular); + location::nearby::windows::Mutex& mutex_ = mutex_actual_; + location::nearby::windows::ConditionVariable condition_variable_actual_ = + location::nearby::windows::ConditionVariable(&mutex_); + location::nearby::windows::ConditionVariable& condition_variable_ = + condition_variable_actual_; + }; +}; + +TEST_F(ConditionVariableTests, SuccessfulCreation) { + // Arrange + ConditionVariableTest conditionVariableTest; + + auto result = conditionVariableTest.WaitForEvent(false, nullptr); + + Sleep(1); + + // Act + conditionVariableTest.PostEvent(); + + // Assert + ASSERT_TRUE(result.get()); +} + +TEST_F(ConditionVariableTests, TimedCreation) { + // Arrange + ConditionVariableTest conditionVariableTest; + const absl::Duration duration = absl::Milliseconds(100); + + // Act + auto result = conditionVariableTest.WaitForEvent(true, &duration); + + // Assert + ASSERT_FALSE(result.get()); // Timed out + + // Act + result = conditionVariableTest.WaitForEvent(true, &duration); + + Sleep(1); + + conditionVariableTest.PostEvent(); + + // Assert + ASSERT_TRUE(result.get()); // Didn't timeout +} diff --git a/cpp/platform/impl/windows/mutex.cc b/cpp/platform/impl/windows/mutex.cc new file mode 100644 index 00000000..d508310e --- /dev/null +++ b/cpp/platform/impl/windows/mutex.cc @@ -0,0 +1,90 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "platform/impl/windows/mutex.h" + +namespace location { +namespace nearby { +namespace windows { + +// A lock is a tool for controlling access to a shared resource by multiple +// threads. +// +// https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/Lock.html +Mutex::Mutex(Mutex::Mode mode) + : mode_(mode), owning_thread_(std::this_thread::get_id()) { + InitializeCriticalSection(&critical_section_); +} + +void Mutex::Lock() { + EnterCriticalSection(&critical_section_); + + std::thread::id currentThread = std::this_thread::get_id(); + + if ((mode_ == Mutex::Mode::kRegular || + mode_ == Mutex::Mode::kRegularNoCheck) && + !locked_) { + mutex_actual_.lock(); + owning_thread_ = currentThread; + locked_ = true; + } + + if (mode_ == Mutex::Mode::kRecursive) { + if (!locked_) { + owning_thread_ = currentThread; + } + if (owning_thread_ == currentThread) { + try { + recursive_mutex_actual_.lock(); + } catch ([[maybe_unused]] const std::system_error& e) { + // Eat the exception and fail silently, argument left for debug + } + locked_ = true; + } + } + + LeaveCriticalSection(&critical_section_); +} + +void Mutex::Unlock() { + EnterCriticalSection(&critical_section_); + + std::thread::id currentThread = std::this_thread::get_id(); + + if (mode_ == Mutex::Mode::kRegular || mode_ == Mutex::Mode::kRegularNoCheck) { + if (currentThread == owning_thread_) { + mutex_actual_.unlock(); + locked_ = false; + } + } + + if (mode_ == Mutex::Mode::kRecursive) { + if (currentThread == owning_thread_) { + recursive_mutex_actual_.unlock(); + locked_ = false; + } + } + + LeaveCriticalSection(&critical_section_); +} + +std::mutex& Mutex::GetWindowsMutex() { return mutex_; } + +std::recursive_mutex& Mutex::GetWindowsRecursiveMutex() { + return recursive_mutex_; +} + +} // namespace windows +} // namespace nearby +} // namespace location diff --git a/cpp/platform/impl/windows/mutex.h b/cpp/platform/impl/windows/mutex.h index 2fee6a2f..88ce5e6e 100644 --- a/cpp/platform/impl/windows/mutex.h +++ b/cpp/platform/impl/windows/mutex.h @@ -1,4 +1,4 @@ -// Copyright 2020 Google LLC +// Copyright 2021 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,6 +15,14 @@ #ifndef PLATFORM_IMPL_WINDOWS_MUTEX_H_ #define PLATFORM_IMPL_WINDOWS_MUTEX_H_ +#include +#include +#include + +#include +#include // NOLINT + +#include "absl/memory/memory.h" #include "platform/api/mutex.h" namespace location { @@ -27,13 +35,28 @@ namespace windows { // https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/Lock.html class Mutex : public api::Mutex { public: - // TODO(b/184975123): replace with real implementation. + Mutex(Mutex::Mode mode); + ~Mutex() override = default; - // TODO(b/184975123): replace with real implementation. - void Lock() override {} - // TODO(b/184975123): replace with real implementation. - void Unlock() override {} + void Lock() override; + + void Unlock() override; + + std::mutex& GetWindowsMutex(); + + std::recursive_mutex& GetWindowsRecursiveMutex(); + + private: + Mutex::Mode mode_; + bool locked_ = false; + + std::mutex mutex_actual_; + std::mutex& mutex_ = mutex_actual_; + std::recursive_mutex recursive_mutex_actual_; + std::recursive_mutex& recursive_mutex_ = recursive_mutex_actual_; + std::thread::id owning_thread_; + CRITICAL_SECTION critical_section_; }; } // namespace windows diff --git a/cpp/platform/impl/windows/mutex_test.cc b/cpp/platform/impl/windows/mutex_test.cc new file mode 100644 index 00000000..d66d52cf --- /dev/null +++ b/cpp/platform/impl/windows/mutex_test.cc @@ -0,0 +1,109 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "platform/impl/windows/mutex.h" + +#include // NOLINT + +#include "gtest/gtest.h" + +class MutexTests : public testing::Test { + public: + class MutexTest { + public: + MutexTest(location::nearby::windows::Mutex& mutex) : mutex_(mutex) {} + + std::future WaitForLock() { // NOLINT + return std::async( + std::launch::async, + // for this lambda you need C++14 + [this]() mutable { + std::unique_lock lck(mutex_.GetWindowsMutex()); + return true; + }); + } + + void PostEvent() { + std::lock_guard guard(mutex_.GetWindowsMutex()); + mutex_.Unlock(); + } + + private: + location::nearby::windows::Mutex& mutex_; + }; +}; + +TEST_F(MutexTests, SuccessfulRecursiveCreation) { + // Arrange + location::nearby::windows::Mutex mutex = location::nearby::windows::Mutex( + location::nearby::windows::Mutex::Mode::kRecursive); + + // Act + std::recursive_mutex& actual = mutex.GetWindowsRecursiveMutex(); + + // Assert + ASSERT_TRUE(actual.native_handle() != nullptr); +} + +TEST_F(MutexTests, SuccessfulCreation) { + // Arrange + location::nearby::windows::Mutex mutex( + location::nearby::windows::Mutex::Mode::kRegular); + + // Act + std::mutex& actual = mutex.GetWindowsMutex(); + + // Assert + ASSERT_TRUE(actual.native_handle() != nullptr); +} + +TEST_F(MutexTests, SuccessfulSignal) { + // Arrange + location::nearby::windows::Mutex mutex( + location::nearby::windows::Mutex::Mode::kRegular); + + location::nearby::windows::Mutex& mutexRef = mutex; + MutexTest mutexTest(mutexRef); + + mutex.Lock(); + + // Act + auto result = mutexTest.WaitForLock(); + mutex.Unlock(); + + // Assert + ASSERT_TRUE(result.get()); +} + +TEST_F(MutexTests, SuccessfulRecursiveSignal) { + // Arrange + location::nearby::windows::Mutex mutex( + location::nearby::windows::Mutex::Mode::kRecursive); + + location::nearby::windows::Mutex& mutexRef = mutex; + MutexTest mutexTest(mutexRef); + + mutex.Lock(); + mutex.Lock(); + mutex.Lock(); + + // Act + auto result = mutexTest.WaitForLock(); + mutex.Unlock(); + mutex.Unlock(); + mutex.Unlock(); + + // Assert + ASSERT_TRUE(result.get()); +} diff --git a/cpp/platform/impl/windows/platform.cc b/cpp/platform/impl/windows/platform.cc index aa62ec58..66d5120f 100644 --- a/cpp/platform/impl/windows/platform.cc +++ b/cpp/platform/impl/windows/platform.cc @@ -66,13 +66,13 @@ std::unique_ptr ImplementationPlatform::CreateCountDownLatch( // TODO(b/184975123): replace with real implementation. std::unique_ptr ImplementationPlatform::CreateMutex(Mutex::Mode mode) { - return absl::make_unique(); + return absl::make_unique(mode); } // TODO(b/184975123): replace with real implementation. std::unique_ptr ImplementationPlatform::CreateConditionVariable(Mutex* mutex) { - return std::unique_ptr(new windows::ConditionVariable()); + return absl::make_unique(mutex); } std::unique_ptr ImplementationPlatform::CreateInputFile(