Initial implementation of CountDownLatch

PiperOrigin-RevId: 394478506
This commit is contained in:
jfcarroll
2021-09-02 09:28:30 -07:00
committed by Copybara-Service
parent e721faca4c
commit 17676ac999
9 changed files with 302 additions and 65 deletions
+4
View File
@@ -57,6 +57,7 @@ cc_library(
"bluetooth_classic_server_socket.h",
"bluetooth_classic_socket.h",
"condition_variable.h",
"count_down_latch.h",
"mutex.h",
"server_sync.h",
"webrtc.h",
@@ -96,6 +97,7 @@ cc_library(
"bluetooth_classic_server_socket.cc",
"bluetooth_classic_socket.cc",
"condition_variable.cc",
"count_down_latch.cc",
"mutex.cc",
"platform.cc",
"utils.cc",
@@ -107,6 +109,7 @@ cc_library(
"bluetooth_classic_server_socket.h",
"bluetooth_classic_socket.h",
"condition_variable.h",
"count_down_latch.h",
"mutex.h",
],
compatible_with = ["//buildenv/target:non_prod"],
@@ -150,6 +153,7 @@ cc_test(
"atomic_boolean_test.cc",
"atomic_reference_test.cc",
"condition_variable_test.cc",
"count_down_latch_test.cc",
"crypto_test.cc",
"input_file_test.cc",
"mutex_test.cc",
@@ -0,0 +1,77 @@
// 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/count_down_latch.h"
namespace location {
namespace nearby {
namespace windows {
// A synchronization aid that allows one or more threads to wait until a set of
// operations being performed in other threads completes.
//
// Creates or opens a named or unnamed event object.
// https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CountDownLatch.html
CountDownLatch::CountDownLatch(int count) {
if (count < 0) {
throw(std::invalid_argument("count"));
}
// https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createeventa
h_count_down_latch_event_ =
CreateEvent(NULL, // default security attributes
TRUE, // manual-reset event
FALSE, // initial state is nonsignaled
TEXT("LatchEvent") // object name
);
count_ = count;
}
Exception CountDownLatch::Await() {
// Waits until the specified object is in the signaled state or the time-out
// interval elapses.
// https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitforsingleobject
if (WaitForSingleObject(h_count_down_latch_event_, INFINITE) ==
WAIT_OBJECT_0) {
return Exception{Exception::kSuccess};
}
return Exception{Exception::kFailed};
}
ExceptionOr<bool> CountDownLatch::Await(absl::Duration timeout) {
auto result = WaitForSingleObject(h_count_down_latch_event_,
absl::ToInt64Milliseconds(timeout));
if (result == WAIT_OBJECT_0) {
return ExceptionOr<bool>(true);
}
if (result == WAIT_TIMEOUT) {
return ExceptionOr<bool>{Exception::kTimeout};
}
return ExceptionOr<bool>{Exception::kFailed};
}
void CountDownLatch::CountDown() {
// Decrements (decreases by one) the value of the specified 32-bit variable as
// an atomic operation.
// https://docs.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-interlockeddecrement
InterlockedDecrement(&count_);
if (count_ == 0) {
SetEvent(h_count_down_latch_event_);
}
}
} // namespace windows
} // namespace nearby
} // namespace location
+19 -9
View File
@@ -15,6 +15,9 @@
#ifndef PLATFORM_IMPL_WINDOWS_COUNT_DOWN_LATCH_H_
#define PLATFORM_IMPL_WINDOWS_COUNT_DOWN_LATCH_H_
#include <windows.h>
#include <synchapi.h>
#include "platform/api/count_down_latch.h"
namespace location {
@@ -27,17 +30,24 @@ namespace windows {
// https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CountDownLatch.html
class CountDownLatch : public api::CountDownLatch {
public:
// TODO(b/184975123): replace with real implementation.
~CountDownLatch() override = default;
CountDownLatch(int count);
// TODO(b/184975123): replace with real implementation.
Exception Await() override { return Exception{}; }
// TODO(b/184975123): replace with real implementation.
ExceptionOr<bool> Await(absl::Duration timeout) override {
return Exception{};
~CountDownLatch() override {
if (h_count_down_latch_event_ != NULL) {
CloseHandle(h_count_down_latch_event_);
h_count_down_latch_event_ = NULL;
}
}
// TODO(b/184975123): replace with real implementation.
void CountDown() override{};
Exception Await() override;
ExceptionOr<bool> Await(absl::Duration timeout) override;
void CountDown() override;
private:
HANDLE h_count_down_latch_event_ = NULL;
uint32_t count_;
};
} // namespace windows
@@ -0,0 +1,180 @@
// 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/count_down_latch.h"
#include "gtest/gtest.h"
class CountDownLatchTests : public testing::Test {
public:
class TestData {
public:
std::unique_ptr<location::nearby::windows::CountDownLatch>& countDownLatch;
LONG volatile& count;
};
class CountDownLatchTest {
public:
static DWORD ThreadProcCountDown(LPVOID lpParam) {
TestData* testData = static_cast<TestData*>(lpParam);
Sleep(1);
InterlockedIncrement(&testData->count);
testData->countDownLatch->CountDown();
return 0;
}
static DWORD ThreadProcAwait(LPVOID lpParam) {
TestData* testData = static_cast<TestData*>(lpParam);
Sleep(1);
testData->countDownLatch->Await();
InterlockedIncrement(&testData->count);
return 0;
}
};
CountDownLatchTests() {}
};
TEST_F(CountDownLatchTests, CountDownLatchAwaitSucceeds) {
// Arrange
LONG volatile count = 0;
std::unique_ptr<location::nearby::windows::CountDownLatch> countDownLatch =
std::make_unique<location::nearby::windows::CountDownLatch>(3);
HANDLE hThreads[3];
DWORD dwThreadID;
TestData testData{countDownLatch, count};
// Setup 3 threads
for (int i = 0; i < 3; i++) {
// TODO: More complex scenarios may require use of a parameter
// to the thread procedure, such as an event per thread to
// be used for synchronization.
hThreads[i] = CreateThread(
NULL, // default security
0, // default stack size
CountDownLatchTest::ThreadProcCountDown, // name of the thread function
&testData, // no thread parameters
0, // default startup flags
&dwThreadID);
EXPECT_TRUE(hThreads[i] != NULL);
}
// Act
location::nearby::Exception result = countDownLatch->Await();
//
// Assert
EXPECT_EQ(result.value, location::nearby::Exception::kSuccess);
EXPECT_EQ(count, 3);
}
TEST_F(CountDownLatchTests, CountDownLatchAwaitTimeoutTimesOut) {
// Arrange
LONG volatile count = 0;
std::unique_ptr<location::nearby::windows::CountDownLatch> countDownLatch =
std::make_unique<location::nearby::windows::CountDownLatch>(3);
// Act
location::nearby::ExceptionOr<bool> result =
countDownLatch->Await(absl::Milliseconds(10));
Sleep(20);
// Assert
EXPECT_FALSE(result.GetResult());
EXPECT_EQ(result.GetException().value, location::nearby::Exception::kTimeout);
}
TEST_F(CountDownLatchTests, CountDownLatchAwaitNoTimeoutSucceeds) {
// Arrange
LONG volatile count = 0;
std::unique_ptr<location::nearby::windows::CountDownLatch> countDownLatch =
std::make_unique<location::nearby::windows::CountDownLatch>(3);
TestData testData{countDownLatch, count};
HANDLE hThreads[3];
DWORD dwThreadID;
// Setup 3 threads
for (int i = 0; i < 3; i++) {
// TODO: More complex scenarios may require use of a parameter
// to the thread procedure, such as an event per thread to
// be used for synchronization.
hThreads[i] = CreateThread(
NULL, // default security
0, // default stack size
CountDownLatchTest::ThreadProcCountDown, // name of the thread function
&testData, // no thread parameters
0, // default startup flags
&dwThreadID);
EXPECT_TRUE(hThreads[i] != NULL);
}
WaitForMultipleObjects(3, hThreads, true, INFINITE);
// Act
location::nearby::ExceptionOr<bool> result =
countDownLatch->Await(absl::Milliseconds(100));
// Assert
EXPECT_TRUE(result.GetResult());
EXPECT_EQ(result.GetException().value, location::nearby::Exception::kSuccess);
EXPECT_EQ(count, 3);
}
TEST_F(CountDownLatchTests, CountDownLatchCountDownBeforeAwaitSucceeds) {
// Arrange
LONG volatile count = 0;
std::unique_ptr<location::nearby::windows::CountDownLatch> countDownLatch =
std::make_unique<location::nearby::windows::CountDownLatch>(1);
HANDLE hThread;
DWORD dwThreadID;
TestData testData{countDownLatch, count};
hThread = CreateThread(
NULL, // default security
0, // default stack size
CountDownLatchTest::ThreadProcAwait, // name of the thread function
&testData, // no thread parameters
0, // default startup flags
&dwThreadID);
EXPECT_TRUE(hThread != NULL);
// Act
countDownLatch->CountDown(); // This countdown occurs before the thread has a
// chance to run
if (hThread != NULL) {
WaitForSingleObject(hThread,
INFINITE); // This will wait till the thread exits
}
// Assert
EXPECT_EQ(count, 1);
}
+10 -43
View File
@@ -23,60 +23,27 @@ namespace windows {
//
// 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_);
: mode_(mode) {
}
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;
if (mode_ == Mutex::Mode::kRegular || mode_ == Mutex::Mode::kRegularNoCheck) {
mutex_impl_.lock();
} else {
if (mode_ == Mutex::Mode::kRecursive) {
recursive_mutex_impl_.lock();
}
}
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;
mutex_impl_.unlock();
} else {
if (mode_ == Mutex::Mode::kRecursive) {
recursive_mutex_impl_.unlock();
}
}
if (mode_ == Mutex::Mode::kRecursive) {
if (currentThread == owning_thread_) {
recursive_mutex_actual_.unlock();
locked_ = false;
}
}
LeaveCriticalSection(&critical_section_);
}
std::mutex& Mutex::GetWindowsMutex() { return mutex_; }
+9 -7
View File
@@ -49,14 +49,16 @@ class Mutex : public api::Mutex {
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_;
std::mutex mutex_impl_; // The actual mutex allocation
std::mutex& mutex_ =
mutex_impl_; // This is passed to other windows functions, must be by
// reference to avoid ownership problems
std::recursive_mutex recursive_mutex_impl_; // The actual mutex allocation
std::recursive_mutex& recursive_mutex_ =
recursive_mutex_impl_; // This is passed to other windows functions,
// must be by reference to avoid ownership
// problems
};
} // namespace windows
+1 -5
View File
@@ -46,13 +46,11 @@ std::string GetPayloadPath(PayloadId payload_id) {
}
} // namespace
// TODO(b/184975123): replace with real implementation.
std::unique_ptr<AtomicBoolean> ImplementationPlatform::CreateAtomicBoolean(
bool initial_value) {
return absl::make_unique<windows::AtomicBoolean>();
}
// TODO(b/184975123): replace with real implementation.
std::unique_ptr<AtomicUint32> ImplementationPlatform::CreateAtomicUint32(
std::uint32_t value) {
return absl::make_unique<windows::AtomicUint32>();
@@ -61,15 +59,13 @@ std::unique_ptr<AtomicUint32> ImplementationPlatform::CreateAtomicUint32(
// TODO(b/184975123): replace with real implementation.
std::unique_ptr<CountDownLatch> ImplementationPlatform::CreateCountDownLatch(
std::int32_t count) {
return absl::make_unique<windows::CountDownLatch>();
return absl::make_unique<windows::CountDownLatch>(count);
}
// TODO(b/184975123): replace with real implementation.
std::unique_ptr<Mutex> ImplementationPlatform::CreateMutex(Mutex::Mode mode) {
return absl::make_unique<windows::Mutex>(mode);
}
// TODO(b/184975123): replace with real implementation.
std::unique_ptr<ConditionVariable>
ImplementationPlatform::CreateConditionVariable(Mutex* mutex) {
return absl::make_unique<location::nearby::windows::ConditionVariable>(mutex);
+1 -1
View File
@@ -15,7 +15,7 @@
#ifndef PLATFORM_IMPL_WINDOWS_UTILS_H_
#define PLATFORM_IMPL_WINDOWS_UTILS_H_
#include <Windows.h>
#include <windows.h>
#include <stdio.h>
#include <string>