Initial implementation of ConditionVariable and Mutex

PiperOrigin-RevId: 394293910
This commit is contained in:
jfcarroll
2021-09-01 12:56:04 -07:00
committed by Copybara-Service
parent 2a629b2c1b
commit e895f3c100
11 changed files with 554 additions and 40 deletions
+8
View File
@@ -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"],
@@ -14,8 +14,12 @@
#include "platform/impl/windows/bluetooth_classic_medium.h"
#include <Windows.h>
#include <stdio.h>
#include <codecvt>
#include <locale>
#include <regex> // NOLINT
#include <string>
#include "platform/base/cancellation_flag.h"
@@ -116,12 +120,22 @@ std::unique_ptr<api::BluetoothSocket> 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<BluetoothDevice*>(&remote_device);
@@ -15,9 +15,6 @@
#ifndef PLATFORM_IMPL_WINDOWS_BLUETOOTH_CLASSIC_MEDIUM_H_
#define PLATFORM_IMPL_WINDOWS_BLUETOOTH_CLASSIC_MEDIUM_H_
#include <Windows.h>
#include <stdio.h>
#include "platform/api/bluetooth_classic.h"
#include "platform/impl/windows/bluetooth_classic_device.h"
#include "platform/impl/windows/bluetooth_classic_server_socket.h"
@@ -14,54 +14,68 @@
#include "platform/impl/windows/bluetooth_classic_medium.h"
#include <Windows.h>
#include <windows.h>
#include <synchapi.h>
#include <string>
#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<std::string, location::nearby::api::BluetoothDevice*>
deviceList;
void device_discovered_cb(location::nearby::api::BluetoothDevice& device) {
const std::string address = device.GetMacAddress();
using location::nearby::windows::BluetoothDevice;
std::map<std::string, location::nearby::api::BluetoothDevice*>::const_iterator
it = deviceList.find(address);
typedef std::map<const std::string, location::nearby::api::BluetoothDevice*>
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<std::string,
location::nearby::api::BluetoothDevice*>::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<location::nearby::windows::BluetoothClassicMedium>
bluetoothClassicMedium =
std::make_unique<location::nearby::windows::BluetoothClassicMedium>(
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<location::nearby::windows::BluetoothClassicMedium>
bluetoothClassicMedium =
std::make_unique<location::nearby::windows::BluetoothClassicMedium>(
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<location::nearby::windows::BluetoothClassicMedium>
bluetoothClassicMedium =
std::make_unique<location::nearby::windows::BluetoothClassicMedium>(
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);
}
@@ -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<windows::Mutex&>(*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<std::mutex> lock =
std::unique_lock<std::mutex>(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<std::mutex> lock =
std::unique_lock<std::mutex>(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
+21 -7
View File
@@ -15,7 +15,14 @@
#ifndef PLATFORM_IMPL_WINDOWS_CONDITION_VARIABLE_H_
#define PLATFORM_IMPL_WINDOWS_CONDITION_VARIABLE_H_
#include <windows.h>
#include <stdio.h>
#include <synchapi.h>
#include <mutex> // 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<std::mutex> lock;
CRITICAL_SECTION critical_section_;
};
} // namespace windows
@@ -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 <future> // 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<bool> 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<std::mutex> 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
}
+90
View File
@@ -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
+29 -6
View File
@@ -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 <windows.h>
#include <stdio.h>
#include <synchapi.h>
#include <memory>
#include <mutex> // 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
+109
View File
@@ -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 <future> // NOLINT
#include "gtest/gtest.h"
class MutexTests : public testing::Test {
public:
class MutexTest {
public:
MutexTest(location::nearby::windows::Mutex& mutex) : mutex_(mutex) {}
std::future<bool> WaitForLock() { // NOLINT
return std::async(
std::launch::async,
// for this lambda you need C++14
[this]() mutable {
std::unique_lock<std::mutex> lck(mutex_.GetWindowsMutex());
return true;
});
}
void PostEvent() {
std::lock_guard<std::mutex> 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());
}
+2 -2
View File
@@ -66,13 +66,13 @@ std::unique_ptr<CountDownLatch> ImplementationPlatform::CreateCountDownLatch(
// TODO(b/184975123): replace with real implementation.
std::unique_ptr<Mutex> ImplementationPlatform::CreateMutex(Mutex::Mode mode) {
return absl::make_unique<windows::Mutex>();
return absl::make_unique<windows::Mutex>(mode);
}
// TODO(b/184975123): replace with real implementation.
std::unique_ptr<ConditionVariable>
ImplementationPlatform::CreateConditionVariable(Mutex* mutex) {
return std::unique_ptr<ConditionVariable>(new windows::ConditionVariable());
return absl::make_unique<location::nearby::windows::ConditionVariable>(mutex);
}
std::unique_ptr<InputFile> ImplementationPlatform::CreateInputFile(