pw_sync: Adds semaphores & mutexes
Adds the BinarySemaphore, CountingSemaphore, and Mutex to the
pw_sync module along with an STL backend for each.
Change-Id: I54a3a64e702202a319ed2c9068bf37412d3fd240
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/24241
Reviewed-by: Ewout van Bekkum <ewout@google.com>
Reviewed-by: Wyatt Hepler <hepler@google.com>
Reviewed-by: Keir Mierle <keir@google.com>
Commit-Queue: Ewout van Bekkum <ewout@google.com>
diff --git a/pw_sync/BUILD b/pw_sync/BUILD
index ceb60c6..18d1962 100644
--- a/pw_sync/BUILD
+++ b/pw_sync/BUILD
@@ -23,9 +23,105 @@
licenses(["notice"]) # Apache License 2.0
# TODO(pwbug/101): Need to add support for facades/backends to Bazel.
+PW_SYNC_BINARY_SEMAPHORE_BACKEND = "//pw_sync_stl:binary_semaphore"
+PW_SYNC_COUNTING_SEMAPHORE_BACKEND = "//pw_sync_stl:counting_semaphore"
+PW_SYNC_MUTEX_BACKEND = "//pw_sync_stl:mutex"
PW_SYNC_SPIN_LOCK_BACKEND = "//pw_sync_stl:spin_lock"
pw_cc_library(
+ name = "binary_semaphore_facade",
+ hdrs = [
+ "public/pw_sync/binary_semaphore.h",
+ ],
+ includes = ["public"],
+ srcs = [
+ "binary_semaphore.cc"
+ ],
+ deps = [
+ PW_SYNC_BINARY_SEMAPHORE_BACKEND + "_headers",
+ "//pw_chrono:system_clock",
+ "//pw_preprocessor",
+ ],
+)
+
+pw_cc_library(
+ name = "binary_semaphore",
+ deps = [
+ ":binary_semaphore_facade",
+ PW_SYNC_BINARY_SEMAPHORE_BACKEND + "_headers",
+ ],
+)
+
+pw_cc_library(
+ name = "binary_semaphore_backend",
+ deps = [
+ PW_SYNC_BINARY_SEMAPHORE_BACKEND,
+ ],
+)
+
+pw_cc_library(
+ name = "counting_semaphore_facade",
+ hdrs = [
+ "public/pw_sync/counting_semaphore.h",
+ ],
+ includes = ["public"],
+ srcs = [
+ "counting_semaphore.cc"
+ ],
+ deps = [
+ PW_SYNC_COUNTING_SEMAPHORE_BACKEND + "_headers",
+ "//pw_chrono:system_clock",
+ "//pw_preprocessor",
+ ],
+)
+
+pw_cc_library(
+ name = "counting_semaphore",
+ deps = [
+ ":counting_semaphore_facade",
+ PW_SYNC_COUNTING_SEMAPHORE_BACKEND + "_headers",
+ ],
+)
+
+pw_cc_library(
+ name = "counting_semaphore_backend",
+ deps = [
+ PW_SYNC_COUNTING_SEMAPHORE_BACKEND,
+ ],
+)
+
+pw_cc_library(
+ name = "mutex_facade",
+ hdrs = [
+ "public/pw_sync/mutex.h",
+ ],
+ includes = ["public"],
+ srcs = [
+ "mutex.cc"
+ ],
+ deps = [
+ PW_SYNC_MUTEX_BACKEND + "_headers",
+ "//pw_chrono:system_clock",
+ "//pw_preprocessor",
+ ],
+)
+
+pw_cc_library(
+ name = "mutex",
+ deps = [
+ ":mutex_facade",
+ PW_SYNC_MUTEX_BACKEND + "_headers",
+ ],
+)
+
+pw_cc_library(
+ name = "mutex_backend",
+ deps = [
+ PW_SYNC_MUTEX_BACKEND,
+ ],
+)
+
+pw_cc_library(
name = "spin_lock_facade",
hdrs = [
"public/pw_sync/spin_lock.h",
@@ -64,6 +160,45 @@
)
pw_cc_test(
+ name = "binary_semaphore_facade_test",
+ srcs = [
+ "binary_semaphore_facade_test.cc",
+ "binary_semaphore_facade_test_c.c",
+ ],
+ deps = [
+ ":binary_semaphore",
+ "//pw_preprocessor",
+ "//pw_unit_test",
+ ],
+)
+
+pw_cc_test(
+ name = "counting_semaphore_facade_test",
+ srcs = [
+ "counting_semaphore_facade_test.cc",
+ "counting_semaphore_facade_test_c.c",
+ ],
+ deps = [
+ ":counting_semaphore",
+ "//pw_preprocessor",
+ "//pw_unit_test",
+ ],
+)
+
+pw_cc_test(
+ name = "mutex_facade_test",
+ srcs = [
+ "mutex_facade_test.cc",
+ "mutex_facade_test_c.c",
+ ],
+ deps = [
+ ":mutex",
+ "//pw_preprocessor",
+ "//pw_unit_test",
+ ],
+)
+
+pw_cc_test(
name = "spin_lock_facade_test",
srcs = [
"spin_lock_facade_test.cc",
diff --git a/pw_sync/BUILD.gn b/pw_sync/BUILD.gn
index cf292b8..058c4f0 100644
--- a/pw_sync/BUILD.gn
+++ b/pw_sync/BUILD.gn
@@ -20,6 +20,15 @@
import("$dir_pw_unit_test/test.gni")
declare_args() {
+ # Backend for the pw_sync module's binary semaphore.
+ pw_sync_BINARY_SEMAPHORE_BACKEND = ""
+
+ # Backend for the pw_sync module's counting semaphore.
+ pw_sync_COUNTING_SEMAPHORE_BACKEND = ""
+
+ # Backend for the pw_sync module's mutex.
+ pw_sync_MUTEX_BACKEND = ""
+
# Backend for the pw_sync module's spin lock.
pw_sync_SPIN_LOCK_BACKEND = ""
}
@@ -29,6 +38,39 @@
visibility = [ ":*" ]
}
+pw_facade("binary_semaphore") {
+ backend = pw_sync_BINARY_SEMAPHORE_BACKEND
+ public_configs = [ ":public_include_path" ]
+ public = [ "public/pw_sync/binary_semaphore.h" ]
+ public_deps = [
+ "$dir_pw_chrono:system_clock",
+ "$dir_pw_preprocessor",
+ ]
+ sources = [ "binary_semaphore.cc" ]
+}
+
+pw_facade("counting_semaphore") {
+ backend = pw_sync_COUNTING_SEMAPHORE_BACKEND
+ public_configs = [ ":public_include_path" ]
+ public = [ "public/pw_sync/counting_semaphore.h" ]
+ public_deps = [
+ "$dir_pw_chrono:system_clock",
+ "$dir_pw_preprocessor",
+ ]
+ sources = [ "counting_semaphore.cc" ]
+}
+
+pw_facade("mutex") {
+ backend = pw_sync_MUTEX_BACKEND
+ public_configs = [ ":public_include_path" ]
+ public = [ "public/pw_sync/mutex.h" ]
+ public_deps = [
+ "$dir_pw_chrono:system_clock",
+ "$dir_pw_preprocessor",
+ ]
+ sources = [ "mutex.cc" ]
+}
+
pw_facade("spin_lock") {
backend = pw_sync_SPIN_LOCK_BACKEND
public_configs = [ ":public_include_path" ]
@@ -37,15 +79,59 @@
sources = [ "spin_lock.cc" ]
}
-pw_test_group("tests") {
- tests = [ ":spin_lock_facade_test" ]
-}
-
pw_source_set("yield_core") {
public = [ "public/pw_sync/yield_core.h" ]
public_configs = [ ":public_include_path" ]
}
+pw_test_group("tests") {
+ tests = [
+ ":binary_semaphore_facade_test",
+ ":counting_semaphore_facade_test",
+ ":mutex_facade_test",
+ ":spin_lock_facade_test",
+ ]
+}
+
+pw_test("binary_semaphore_facade_test") {
+ enable_if = pw_sync_BINARY_SEMAPHORE_BACKEND != ""
+ sources = [
+ "binary_semaphore_facade_test.cc",
+ "binary_semaphore_facade_test_c.c",
+ ]
+ deps = [
+ ":binary_semaphore",
+ "$dir_pw_preprocessor",
+ pw_sync_BINARY_SEMAPHORE_BACKEND,
+ ]
+}
+
+pw_test("counting_semaphore_facade_test") {
+ enable_if = pw_sync_COUNTING_SEMAPHORE_BACKEND != ""
+ sources = [
+ "counting_semaphore_facade_test.cc",
+ "counting_semaphore_facade_test_c.c",
+ ]
+ deps = [
+ ":counting_semaphore",
+ "$dir_pw_preprocessor",
+ pw_sync_COUNTING_SEMAPHORE_BACKEND,
+ ]
+}
+
+pw_test("mutex_facade_test") {
+ enable_if = pw_sync_MUTEX_BACKEND != ""
+ sources = [
+ "mutex_facade_test.cc",
+ "mutex_facade_test_c.c",
+ ]
+ deps = [
+ ":mutex",
+ "$dir_pw_preprocessor",
+ pw_sync_MUTEX_BACKEND,
+ ]
+}
+
pw_test("spin_lock_facade_test") {
enable_if = pw_sync_SPIN_LOCK_BACKEND != ""
sources = [
diff --git a/pw_sync/binary_semaphore.cc b/pw_sync/binary_semaphore.cc
new file mode 100644
index 0000000..439504b
--- /dev/null
+++ b/pw_sync/binary_semaphore.cc
@@ -0,0 +1,49 @@
+// Copyright 2020 The Pigweed Authors
+//
+// 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 "pw_sync/binary_semaphore.h"
+
+using pw::chrono::SystemClock;
+
+extern "C" void pw_sync_BinarySemaphore_Release(
+ pw_sync_BinarySemaphore* semaphore) {
+ semaphore->release();
+}
+
+extern "C" void pw_sync_BinarySemaphore_Acquire(
+ pw_sync_BinarySemaphore* semaphore) {
+ semaphore->acquire();
+}
+
+extern "C" bool pw_sync_BinarySemaphore_TryAcquire(
+ pw_sync_BinarySemaphore* semaphore) {
+ return semaphore->try_acquire();
+}
+
+extern "C" bool pw_sync_BinarySemaphore_TryAcquireFor(
+ pw_sync_BinarySemaphore* semaphore,
+ pw_chrono_SystemClock_TickCount for_at_least) {
+ return semaphore->try_acquire_for(SystemClock::duration(for_at_least));
+}
+
+extern "C" bool pw_sync_BinarySemaphore_TryAcquireUntil(
+ pw_sync_BinarySemaphore* semaphore,
+ pw_chrono_SystemClock_TimePoint until_at_least) {
+ return semaphore->try_acquire_until(SystemClock::time_point(
+ SystemClock::duration(until_at_least.ticks_since_epoch)));
+}
+
+extern "C" ptrdiff_t pw_sync_BinarySemaphore_Max(void) {
+ return pw::sync::BinarySemaphore::max();
+}
diff --git a/pw_sync/binary_semaphore_facade_test.cc b/pw_sync/binary_semaphore_facade_test.cc
new file mode 100644
index 0000000..7c2273c
--- /dev/null
+++ b/pw_sync/binary_semaphore_facade_test.cc
@@ -0,0 +1,170 @@
+// Copyright 2020 The Pigweed Authors
+//
+// 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 <chrono>
+
+#include "gtest/gtest.h"
+#include "pw_chrono/system_clock.h"
+#include "pw_sync/binary_semaphore.h"
+
+using pw::chrono::SystemClock;
+
+namespace pw::sync {
+namespace {
+
+extern "C" {
+
+// Functions defined in binary_semaphore_facade_test_c.c which call the API
+// from C.
+void pw_sync_BinarySemaphore_CallRelease(pw_sync_BinarySemaphore* semaphore);
+void pw_sync_BinarySemaphore_CallAcquire(pw_sync_BinarySemaphore* semaphore);
+bool pw_sync_BinarySemaphore_CallTryAcquire(pw_sync_BinarySemaphore* semaphore);
+bool pw_sync_BinarySemaphore_CallTryAcquireFor(
+ pw_sync_BinarySemaphore* semaphore,
+ pw_chrono_SystemClock_TickCount for_at_least);
+bool pw_sync_BinarySemaphore_CallTryAcquireUntil(
+ pw_sync_BinarySemaphore* semaphore,
+ pw_chrono_SystemClock_TimePoint until_at_least);
+ptrdiff_t pw_sync_BinarySemaphore_CallMax(void);
+
+} // extern "C"
+
+static constexpr auto kArbitraryDuration = std::chrono::milliseconds(42);
+// We can't control the SystemClock's period configuration, so just in case
+// duration cannot be accurately expressed in integer ticks, round the
+// duration w/ duration_cast.
+static constexpr auto kRoundedArbitraryDuration =
+ std::chrono::duration_cast<SystemClock::duration>(kArbitraryDuration);
+static constexpr pw_chrono_SystemClock_TickCount kRoundedArbitraryDurationInC =
+ kRoundedArbitraryDuration.count();
+
+TEST(BinarySemaphore, EmptyInitialState) {
+ BinarySemaphore semaphore;
+ EXPECT_FALSE(semaphore.try_acquire());
+}
+
+// TODO(pwbug/291): Add real concurrency tests once we have pw::thread.
+
+TEST(BinarySemaphore, Release) {
+ BinarySemaphore semaphore;
+ semaphore.release();
+ semaphore.release();
+ semaphore.acquire();
+ // Ensure it fails when empty.
+ EXPECT_FALSE(semaphore.try_acquire());
+}
+
+BinarySemaphore empty_initial_semaphore;
+TEST(BinarySemaphore, EmptyInitialStateStatic) {
+ EXPECT_FALSE(empty_initial_semaphore.try_acquire());
+}
+
+BinarySemaphore release_semaphore;
+TEST(BinarySemaphore, ReleaseStatic) {
+ release_semaphore.release();
+ release_semaphore.release();
+ release_semaphore.acquire();
+ // Ensure it fails when empty.
+ EXPECT_FALSE(release_semaphore.try_acquire());
+}
+
+TEST(BinarySemaphore, TryAcquireFor) {
+ BinarySemaphore semaphore;
+ semaphore.release();
+
+ SystemClock::time_point before = SystemClock::now();
+ EXPECT_TRUE(semaphore.try_acquire_for(kRoundedArbitraryDuration));
+ SystemClock::duration time_elapsed = SystemClock::now() - before;
+ EXPECT_LT(time_elapsed, kRoundedArbitraryDuration);
+
+ // Ensure it blocks and fails when empty.
+ before = SystemClock::now();
+ EXPECT_FALSE(semaphore.try_acquire_for(kRoundedArbitraryDuration));
+ time_elapsed = SystemClock::now() - before;
+ EXPECT_GE(time_elapsed, kRoundedArbitraryDuration);
+}
+
+TEST(BinarySemaphore, TryAcquireUntil) {
+ BinarySemaphore semaphore;
+ semaphore.release();
+
+ const SystemClock::time_point deadline =
+ SystemClock::now() + kRoundedArbitraryDuration;
+ EXPECT_TRUE(semaphore.try_acquire_until(deadline));
+ EXPECT_LT(SystemClock::now(), deadline);
+
+ // Ensure it blocks and fails when empty.
+ EXPECT_FALSE(semaphore.try_acquire_until(deadline));
+ EXPECT_GE(SystemClock::now(), deadline);
+}
+
+TEST(BinarySemaphore, EmptyInitialStateInC) {
+ BinarySemaphore semaphore;
+ EXPECT_FALSE(pw_sync_BinarySemaphore_CallTryAcquire(&semaphore));
+}
+
+TEST(BinarySemaphore, ReleaseInC) {
+ BinarySemaphore semaphore;
+ pw_sync_BinarySemaphore_CallRelease(&semaphore);
+ pw_sync_BinarySemaphore_CallRelease(&semaphore);
+ pw_sync_BinarySemaphore_CallAcquire(&semaphore);
+ // Ensure it fails when empty.
+ EXPECT_FALSE(pw_sync_BinarySemaphore_CallTryAcquire(&semaphore));
+}
+
+TEST(BinarySemaphore, TryAcquireForInC) {
+ BinarySemaphore semaphore;
+ pw_sync_BinarySemaphore_CallRelease(&semaphore);
+
+ pw_chrono_SystemClock_TimePoint before = pw_chrono_SystemClock_Now();
+ ASSERT_TRUE(pw_sync_BinarySemaphore_CallTryAcquireFor(
+ &semaphore, kRoundedArbitraryDurationInC));
+ pw_chrono_SystemClock_TickCount time_elapsed =
+ pw_chrono_SystemClock_Now().ticks_since_epoch - before.ticks_since_epoch;
+ EXPECT_LT(time_elapsed, kRoundedArbitraryDurationInC);
+
+ // Ensure it blocks and fails when empty.
+ before = pw_chrono_SystemClock_Now();
+ EXPECT_FALSE(pw_sync_BinarySemaphore_CallTryAcquireFor(
+ &semaphore, kRoundedArbitraryDurationInC));
+ time_elapsed =
+ pw_chrono_SystemClock_Now().ticks_since_epoch - before.ticks_since_epoch;
+ EXPECT_GE(time_elapsed, kRoundedArbitraryDurationInC);
+}
+
+TEST(BinarySemaphore, TryAcquireUntilInC) {
+ BinarySemaphore semaphore;
+ pw_sync_BinarySemaphore_CallRelease(&semaphore);
+
+ pw_chrono_SystemClock_TimePoint deadline;
+ deadline.ticks_since_epoch = pw_chrono_SystemClock_Now().ticks_since_epoch +
+ kRoundedArbitraryDurationInC;
+ ASSERT_TRUE(
+ pw_sync_BinarySemaphore_CallTryAcquireUntil(&semaphore, deadline));
+ EXPECT_LT(pw_chrono_SystemClock_Now().ticks_since_epoch,
+ deadline.ticks_since_epoch);
+
+ // Ensure it blocks and fails when empty.
+ EXPECT_FALSE(
+ pw_sync_BinarySemaphore_CallTryAcquireUntil(&semaphore, deadline));
+ EXPECT_GE(pw_chrono_SystemClock_Now().ticks_since_epoch,
+ deadline.ticks_since_epoch);
+}
+
+TEST(BinarySemaphore, MaxInC) {
+ EXPECT_EQ(BinarySemaphore::max(), pw_sync_BinarySemaphore_Max());
+}
+
+} // namespace
+} // namespace pw::sync
diff --git a/pw_sync/binary_semaphore_facade_test_c.c b/pw_sync/binary_semaphore_facade_test_c.c
new file mode 100644
index 0000000..5145301
--- /dev/null
+++ b/pw_sync/binary_semaphore_facade_test_c.c
@@ -0,0 +1,49 @@
+// Copyright 2020 The Pigweed Authors
+//
+// 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.
+
+// These tests call the pw_sync module counting_semaphore API from C. The return
+// values are checked in the main C++ tests.
+
+#include <stdbool.h>
+
+#include "pw_sync/binary_semaphore.h"
+
+void pw_sync_BinarySemaphore_CallRelease(pw_sync_BinarySemaphore* semaphore) {
+ pw_sync_BinarySemaphore_Release(semaphore);
+}
+
+void pw_sync_BinarySemaphore_CallAcquire(pw_sync_BinarySemaphore* semaphore) {
+ pw_sync_BinarySemaphore_Acquire(semaphore);
+}
+
+bool pw_sync_BinarySemaphore_CallTryAcquire(
+ pw_sync_BinarySemaphore* semaphore) {
+ return pw_sync_BinarySemaphore_TryAcquire(semaphore);
+}
+
+bool pw_sync_BinarySemaphore_CallTryAcquireFor(
+ pw_sync_BinarySemaphore* semaphore,
+ pw_chrono_SystemClock_TickCount for_at_least) {
+ return pw_sync_BinarySemaphore_TryAcquireFor(semaphore, for_at_least);
+}
+
+bool pw_sync_BinarySemaphore_CallTryAcquireUntil(
+ pw_sync_BinarySemaphore* semaphore,
+ pw_chrono_SystemClock_TimePoint until_at_least) {
+ return pw_sync_BinarySemaphore_TryAcquireUntil(semaphore, until_at_least);
+}
+
+ptrdiff_t pw_sync_BinarySemaphore_CallMax(void) {
+ return pw_sync_BinarySemaphore_Max();
+}
diff --git a/pw_sync/counting_semaphore.cc b/pw_sync/counting_semaphore.cc
new file mode 100644
index 0000000..9915e5e
--- /dev/null
+++ b/pw_sync/counting_semaphore.cc
@@ -0,0 +1,54 @@
+// Copyright 2020 The Pigweed Authors
+//
+// 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 "pw_sync/counting_semaphore.h"
+
+using pw::chrono::SystemClock;
+
+extern "C" void pw_sync_CountingSemaphore_Release(
+ pw_sync_CountingSemaphore* semaphore) {
+ semaphore->release();
+}
+
+extern "C" void pw_sync_CountingSemaphore_ReleaseNum(
+ pw_sync_CountingSemaphore* semaphore, ptrdiff_t update) {
+ semaphore->release(update);
+}
+
+extern "C" void pw_sync_CountingSemaphore_Acquire(
+ pw_sync_CountingSemaphore* semaphore) {
+ semaphore->acquire();
+}
+
+extern "C" bool pw_sync_CountingSemaphore_TryAcquire(
+ pw_sync_CountingSemaphore* semaphore) {
+ return semaphore->try_acquire();
+}
+
+extern "C" bool pw_sync_CountingSemaphore_TryAcquireFor(
+ pw_sync_CountingSemaphore* semaphore,
+ pw_chrono_SystemClock_TickCount for_at_least) {
+ return semaphore->try_acquire_for(SystemClock::duration(for_at_least));
+}
+
+extern "C" bool pw_sync_CountingSemaphore_TryAcquireUntil(
+ pw_sync_CountingSemaphore* semaphore,
+ pw_chrono_SystemClock_TimePoint until_at_least) {
+ return semaphore->try_acquire_until(SystemClock::time_point(
+ SystemClock::duration(until_at_least.ticks_since_epoch)));
+}
+
+extern "C" ptrdiff_t pw_sync_CountingSemaphore_Max(void) {
+ return pw::sync::CountingSemaphore::max();
+}
diff --git a/pw_sync/counting_semaphore_facade_test.cc b/pw_sync/counting_semaphore_facade_test.cc
new file mode 100644
index 0000000..3d5b331
--- /dev/null
+++ b/pw_sync/counting_semaphore_facade_test.cc
@@ -0,0 +1,200 @@
+// Copyright 2020 The Pigweed Authors
+//
+// 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 <chrono>
+
+#include "gtest/gtest.h"
+#include "pw_chrono/system_clock.h"
+#include "pw_sync/counting_semaphore.h"
+
+using pw::chrono::SystemClock;
+
+namespace pw::sync {
+namespace {
+
+extern "C" {
+
+// Functions defined in counting_semaphore_facade_test_c.c which call the API
+// from C.
+void pw_sync_CountingSemaphore_CallRelease(
+ pw_sync_CountingSemaphore* semaphore);
+void pw_sync_CountingSemaphore_CallReleaseNum(
+ pw_sync_CountingSemaphore* semaphore, ptrdiff_t update);
+void pw_sync_CountingSemaphore_CallAcquire(
+ pw_sync_CountingSemaphore* semaphore);
+bool pw_sync_CountingSemaphore_CallTryAcquire(
+ pw_sync_CountingSemaphore* semaphore);
+bool pw_sync_CountingSemaphore_CallTryAcquireFor(
+ pw_sync_CountingSemaphore* semaphore,
+ pw_chrono_SystemClock_TickCount for_at_least);
+bool pw_sync_CountingSemaphore_CallTryAcquireUntil(
+ pw_sync_CountingSemaphore* semaphore,
+ pw_chrono_SystemClock_TimePoint until_at_least);
+ptrdiff_t pw_sync_CountingSemaphore_CallMax(void);
+
+} // extern "C"
+
+static constexpr auto kArbitraryDuration = std::chrono::milliseconds(42);
+// We can't control the SystemClock's period configuration, so just in case
+// duration cannot be accurately expressed in integer ticks, round the
+// duration w/ duration_cast.
+static constexpr auto kRoundedArbitraryDuration =
+ std::chrono::duration_cast<SystemClock::duration>(kArbitraryDuration);
+static constexpr pw_chrono_SystemClock_TickCount kRoundedArbitraryDurationInC =
+ kRoundedArbitraryDuration.count();
+
+TEST(CountingSemaphore, EmptyInitialState) {
+ CountingSemaphore semaphore;
+ EXPECT_FALSE(semaphore.try_acquire());
+}
+
+// TODO(pwbug/291): Add real concurrency tests once we have pw::thread.
+
+TEST(CountingSemaphore, SingleRelease) {
+ CountingSemaphore semaphore;
+ semaphore.release();
+ semaphore.release();
+ semaphore.acquire();
+ semaphore.acquire();
+ // Ensure it fails when empty.
+ EXPECT_FALSE(semaphore.try_acquire());
+}
+
+CountingSemaphore empty_initial_semaphore;
+TEST(CountingSemaphore, EmptyInitialStateStatic) {
+ EXPECT_FALSE(empty_initial_semaphore.try_acquire());
+}
+
+CountingSemaphore release_semaphore;
+TEST(CountingSemaphore, ReleaseStatic) {
+ release_semaphore.release();
+ release_semaphore.release();
+ release_semaphore.acquire();
+ release_semaphore.acquire();
+ // Ensure it fails when empty.
+ EXPECT_FALSE(release_semaphore.try_acquire());
+}
+
+TEST(CountingSemaphore, MultiRelease) {
+ CountingSemaphore semaphore;
+ semaphore.release(2);
+ semaphore.release(1);
+ semaphore.acquire();
+ semaphore.acquire();
+ semaphore.acquire();
+ // Ensure it fails when empty.
+ EXPECT_FALSE(semaphore.try_acquire());
+}
+
+TEST(CountingSemaphore, TryAcquireFor) {
+ CountingSemaphore semaphore;
+ semaphore.release();
+
+ SystemClock::time_point before = SystemClock::now();
+ EXPECT_TRUE(semaphore.try_acquire_for(kRoundedArbitraryDuration));
+ SystemClock::duration time_elapsed = SystemClock::now() - before;
+ EXPECT_LT(time_elapsed, kRoundedArbitraryDuration);
+
+ // Ensure it blocks and fails when empty.
+ before = SystemClock::now();
+ EXPECT_FALSE(semaphore.try_acquire_for(kRoundedArbitraryDuration));
+ time_elapsed = SystemClock::now() - before;
+ EXPECT_GE(time_elapsed, kRoundedArbitraryDuration);
+}
+
+TEST(CountingSemaphore, TryAcquireUntil) {
+ CountingSemaphore semaphore;
+ semaphore.release();
+
+ const SystemClock::time_point deadline =
+ SystemClock::now() + kRoundedArbitraryDuration;
+ EXPECT_TRUE(semaphore.try_acquire_until(deadline));
+ EXPECT_LT(SystemClock::now(), deadline);
+
+ // Ensure it blocks and fails when empty.
+ EXPECT_FALSE(semaphore.try_acquire_until(deadline));
+ EXPECT_GE(SystemClock::now(), deadline);
+}
+
+TEST(CountingSemaphore, EmptyInitialStateInC) {
+ CountingSemaphore semaphore;
+ EXPECT_FALSE(pw_sync_CountingSemaphore_CallTryAcquire(&semaphore));
+}
+
+TEST(CountingSemaphore, SingeReleaseInC) {
+ CountingSemaphore semaphore;
+ pw_sync_CountingSemaphore_CallRelease(&semaphore);
+ pw_sync_CountingSemaphore_CallRelease(&semaphore);
+ pw_sync_CountingSemaphore_CallAcquire(&semaphore);
+ pw_sync_CountingSemaphore_CallAcquire(&semaphore);
+ // Ensure it fails when empty.
+ EXPECT_FALSE(pw_sync_CountingSemaphore_CallTryAcquire(&semaphore));
+}
+
+TEST(CountingSemaphore, MultiReleaseInC) {
+ CountingSemaphore semaphore;
+ pw_sync_CountingSemaphore_CallReleaseNum(&semaphore, 2);
+ pw_sync_CountingSemaphore_CallReleaseNum(&semaphore, 1);
+ pw_sync_CountingSemaphore_CallAcquire(&semaphore);
+ pw_sync_CountingSemaphore_CallAcquire(&semaphore);
+ pw_sync_CountingSemaphore_CallAcquire(&semaphore);
+ // Ensure it fails when empty.
+ EXPECT_FALSE(pw_sync_CountingSemaphore_CallTryAcquire(&semaphore));
+}
+
+TEST(CountingSemaphore, TryAcquireForInC) {
+ CountingSemaphore semaphore;
+ pw_sync_CountingSemaphore_CallRelease(&semaphore);
+
+ pw_chrono_SystemClock_TimePoint before = pw_chrono_SystemClock_Now();
+ ASSERT_TRUE(pw_sync_CountingSemaphore_CallTryAcquireFor(
+ &semaphore, kRoundedArbitraryDurationInC));
+ pw_chrono_SystemClock_TickCount time_elapsed =
+ pw_chrono_SystemClock_Now().ticks_since_epoch - before.ticks_since_epoch;
+ EXPECT_LT(time_elapsed, kRoundedArbitraryDurationInC);
+
+ // Ensure it blocks and fails when empty.
+ before = pw_chrono_SystemClock_Now();
+ EXPECT_FALSE(pw_sync_CountingSemaphore_CallTryAcquireFor(
+ &semaphore, kRoundedArbitraryDurationInC));
+ time_elapsed =
+ pw_chrono_SystemClock_Now().ticks_since_epoch - before.ticks_since_epoch;
+ EXPECT_GE(time_elapsed, kRoundedArbitraryDurationInC);
+}
+
+TEST(CountingSemaphore, TryAcquireUntilInC) {
+ CountingSemaphore semaphore;
+ pw_sync_CountingSemaphore_CallRelease(&semaphore);
+
+ pw_chrono_SystemClock_TimePoint deadline;
+ deadline.ticks_since_epoch = pw_chrono_SystemClock_Now().ticks_since_epoch +
+ kRoundedArbitraryDurationInC;
+ ASSERT_TRUE(
+ pw_sync_CountingSemaphore_CallTryAcquireUntil(&semaphore, deadline));
+ EXPECT_LT(pw_chrono_SystemClock_Now().ticks_since_epoch,
+ deadline.ticks_since_epoch);
+
+ // Ensure it blocks and fails when empty.
+ EXPECT_FALSE(
+ pw_sync_CountingSemaphore_CallTryAcquireUntil(&semaphore, deadline));
+ EXPECT_GE(pw_chrono_SystemClock_Now().ticks_since_epoch,
+ deadline.ticks_since_epoch);
+}
+
+TEST(CountingSemaphore, MaxInC) {
+ EXPECT_EQ(CountingSemaphore::max(), pw_sync_CountingSemaphore_Max());
+}
+
+} // namespace
+} // namespace pw::sync
diff --git a/pw_sync/counting_semaphore_facade_test_c.c b/pw_sync/counting_semaphore_facade_test_c.c
new file mode 100644
index 0000000..592df9b
--- /dev/null
+++ b/pw_sync/counting_semaphore_facade_test_c.c
@@ -0,0 +1,56 @@
+// Copyright 2020 The Pigweed Authors
+//
+// 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.
+
+// These tests call the pw_sync module counting_semaphore API from C. The return
+// values are checked in the main C++ tests.
+
+#include <stdbool.h>
+
+#include "pw_sync/counting_semaphore.h"
+
+void pw_sync_CountingSemaphore_CallRelease(
+ pw_sync_CountingSemaphore* semaphore) {
+ pw_sync_CountingSemaphore_Release(semaphore);
+}
+
+void pw_sync_CountingSemaphore_CallReleaseNum(
+ pw_sync_CountingSemaphore* semaphore, ptrdiff_t update) {
+ pw_sync_CountingSemaphore_ReleaseNum(semaphore, update);
+}
+
+void pw_sync_CountingSemaphore_CallAcquire(
+ pw_sync_CountingSemaphore* semaphore) {
+ pw_sync_CountingSemaphore_Acquire(semaphore);
+}
+
+bool pw_sync_CountingSemaphore_CallTryAcquire(
+ pw_sync_CountingSemaphore* semaphore) {
+ return pw_sync_CountingSemaphore_TryAcquire(semaphore);
+}
+
+bool pw_sync_CountingSemaphore_CallTryAcquireFor(
+ pw_sync_CountingSemaphore* semaphore,
+ pw_chrono_SystemClock_TickCount for_at_least) {
+ return pw_sync_CountingSemaphore_TryAcquireFor(semaphore, for_at_least);
+}
+
+bool pw_sync_CountingSemaphore_CallTryAcquireUntil(
+ pw_sync_CountingSemaphore* semaphore,
+ pw_chrono_SystemClock_TimePoint until_at_least) {
+ return pw_sync_CountingSemaphore_TryAcquireUntil(semaphore, until_at_least);
+}
+
+ptrdiff_t pw_sync_CountingSemaphore_CallMax(void) {
+ return pw_sync_CountingSemaphore_Max();
+}
diff --git a/pw_sync/mutex.cc b/pw_sync/mutex.cc
new file mode 100644
index 0000000..85c3c92
--- /dev/null
+++ b/pw_sync/mutex.cc
@@ -0,0 +1,35 @@
+// Copyright 2020 The Pigweed Authors
+//
+// 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 "pw_sync/mutex.h"
+
+using pw::chrono::SystemClock;
+
+extern "C" void pw_sync_Mutex_Lock(pw_sync_Mutex* mutex) { mutex->lock(); }
+
+extern "C" bool pw_sync_Mutex_TryLock(pw_sync_Mutex* mutex) {
+ return mutex->try_lock();
+}
+
+extern "C" bool pw_sync_Mutex_TryLockFor(
+ pw_sync_Mutex* mutex, pw_chrono_SystemClock_TickCount for_at_least) {
+ return mutex->try_lock_for(SystemClock::duration(for_at_least));
+}
+
+extern "C" bool pw_sync_Mutex_TryLockUntil(
+ pw_sync_Mutex* mutex, pw_chrono_SystemClock_TimePoint until_at_least) {
+ return mutex->try_lock_until(SystemClock::time_point(
+ SystemClock::duration(until_at_least.ticks_since_epoch)));
+}
+extern "C" void pw_sync_Mutex_Unlock(pw_sync_Mutex* mutex) { mutex->unlock(); }
diff --git a/pw_sync/mutex_facade_test.cc b/pw_sync/mutex_facade_test.cc
new file mode 100644
index 0000000..046682a
--- /dev/null
+++ b/pw_sync/mutex_facade_test.cc
@@ -0,0 +1,160 @@
+// Copyright 2020 The Pigweed Authors
+//
+// 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 <chrono>
+
+#include "gtest/gtest.h"
+#include "pw_chrono/system_clock.h"
+#include "pw_sync/mutex.h"
+
+using pw::chrono::SystemClock;
+
+namespace pw::sync {
+namespace {
+
+extern "C" {
+
+// Functions defined in mutex_facade_test_c.c which call the API from C.
+void pw_sync_Mutex_CallLock(pw_sync_Mutex* mutex);
+bool pw_sync_Mutex_CallTryLock(pw_sync_Mutex* mutex);
+bool pw_sync_Mutex_CallTryLockFor(pw_sync_Mutex* mutex,
+ pw_chrono_SystemClock_TickCount for_at_least);
+bool pw_sync_Mutex_CallTryLockUntil(
+ pw_sync_Mutex* mutex, pw_chrono_SystemClock_TimePoint until_at_least);
+void pw_sync_Mutex_CallUnlock(pw_sync_Mutex* mutex);
+
+} // extern "C"
+
+static constexpr auto kArbitraryDuration = std::chrono::milliseconds(42);
+// We can't control the SystemClock's period configuration, so just in case
+// duration cannot be accurately expressed in integer ticks, round the
+// duration w/ duration_cast.
+static constexpr auto kRoundedArbitraryDuration =
+ std::chrono::duration_cast<SystemClock::duration>(kArbitraryDuration);
+static constexpr pw_chrono_SystemClock_TickCount kRoundedArbitraryDurationInC =
+ kRoundedArbitraryDuration.count();
+
+// TODO(pwbug/291): Add real concurrency tests once we have pw::thread.
+
+TEST(Mutex, LockUnlock) {
+ pw::sync::Mutex mutex;
+ mutex.lock();
+ // Ensure it fails to lock when already held.
+ EXPECT_FALSE(mutex.try_lock());
+ mutex.unlock();
+}
+
+Mutex static_mutex;
+TEST(Mutex, LockUnlockStatic) {
+ static_mutex.lock();
+ // Ensure it fails to lock when already held.
+ EXPECT_FALSE(static_mutex.try_lock());
+ static_mutex.unlock();
+}
+
+TEST(Mutex, TryLockUnlock) {
+ pw::sync::Mutex mutex;
+ ASSERT_TRUE(mutex.try_lock());
+ // Ensure it fails to lock when already held.
+ EXPECT_FALSE(mutex.try_lock());
+ mutex.unlock();
+}
+
+TEST(Mutex, TryLockUnlockFor) {
+ pw::sync::Mutex mutex;
+
+ SystemClock::time_point before = SystemClock::now();
+ ASSERT_TRUE(mutex.try_lock_for(kRoundedArbitraryDuration));
+ SystemClock::duration time_elapsed = SystemClock::now() - before;
+ EXPECT_LT(time_elapsed, kRoundedArbitraryDuration);
+
+ // Ensure it blocks and fails to lock when already held.
+ before = SystemClock::now();
+ EXPECT_FALSE(mutex.try_lock_for(kRoundedArbitraryDuration));
+ time_elapsed = SystemClock::now() - before;
+ EXPECT_GE(time_elapsed, kRoundedArbitraryDuration);
+
+ mutex.unlock();
+}
+
+TEST(Mutex, TryLockUnlockUntil) {
+ pw::sync::Mutex mutex;
+
+ const SystemClock::time_point deadline =
+ SystemClock::now() + kRoundedArbitraryDuration;
+ ASSERT_TRUE(mutex.try_lock_until(deadline));
+ EXPECT_LT(SystemClock::now(), deadline);
+
+ // Ensure it blocks and fails to lock when already held.
+ EXPECT_FALSE(
+ mutex.try_lock_until(SystemClock::now() + kRoundedArbitraryDuration));
+ EXPECT_GE(SystemClock::now(), deadline);
+
+ mutex.unlock();
+}
+
+TEST(Mutex, LockUnlockInC) {
+ pw::sync::Mutex mutex;
+ pw_sync_Mutex_CallLock(&mutex);
+ pw_sync_Mutex_CallUnlock(&mutex);
+}
+
+TEST(Mutex, TryLockUnlockInC) {
+ pw::sync::Mutex mutex;
+ ASSERT_TRUE(pw_sync_Mutex_CallTryLock(&mutex));
+ // Ensure it fails to lock when already held.
+ EXPECT_FALSE(pw_sync_Mutex_CallTryLock(&mutex));
+ pw_sync_Mutex_CallUnlock(&mutex);
+}
+
+TEST(Mutex, TryLockUnlockForInC) {
+ pw::sync::Mutex mutex;
+
+ pw_chrono_SystemClock_TimePoint before = pw_chrono_SystemClock_Now();
+ ASSERT_TRUE(
+ pw_sync_Mutex_CallTryLockFor(&mutex, kRoundedArbitraryDurationInC));
+ pw_chrono_SystemClock_TickCount time_elapsed =
+ pw_chrono_SystemClock_Now().ticks_since_epoch - before.ticks_since_epoch;
+ EXPECT_LT(time_elapsed, kRoundedArbitraryDurationInC);
+
+ // Ensure it blocks and fails to lock when already held.
+ before = pw_chrono_SystemClock_Now();
+ EXPECT_FALSE(
+ pw_sync_Mutex_CallTryLockFor(&mutex, kRoundedArbitraryDurationInC));
+ time_elapsed =
+ pw_chrono_SystemClock_Now().ticks_since_epoch - before.ticks_since_epoch;
+ EXPECT_GE(time_elapsed, kRoundedArbitraryDurationInC);
+
+ pw_sync_Mutex_CallUnlock(&mutex);
+}
+
+TEST(Mutex, TryLockUnlockUntilInC) {
+ pw::sync::Mutex mutex;
+ pw_chrono_SystemClock_TimePoint deadline;
+ deadline.ticks_since_epoch = pw_chrono_SystemClock_Now().ticks_since_epoch +
+ kRoundedArbitraryDurationInC;
+ ASSERT_TRUE(pw_sync_Mutex_CallTryLockUntil(&mutex, deadline));
+ EXPECT_LT(pw_chrono_SystemClock_Now().ticks_since_epoch,
+ deadline.ticks_since_epoch);
+
+ // Ensure it blocks and fails to lock when already held.
+ EXPECT_FALSE(pw_sync_Mutex_CallTryLockUntil(&mutex, deadline));
+ EXPECT_GE(pw_chrono_SystemClock_Now().ticks_since_epoch,
+ deadline.ticks_since_epoch);
+
+ mutex.unlock();
+}
+
+} // namespace
+} // namespace pw::sync
diff --git a/pw_sync/mutex_facade_test_c.c b/pw_sync/mutex_facade_test_c.c
new file mode 100644
index 0000000..9004912
--- /dev/null
+++ b/pw_sync/mutex_facade_test_c.c
@@ -0,0 +1,40 @@
+// Copyright 2020 The Pigweed Authors
+//
+// 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.
+
+// These tests call the pw_sync module mutex API from C. The return values are
+// checked in the main C++ tests.
+
+#include <stdbool.h>
+
+#include "pw_sync/mutex.h"
+
+void pw_sync_Mutex_CallLock(pw_sync_Mutex* mutex) { pw_sync_Mutex_Lock(mutex); }
+
+bool pw_sync_Mutex_CallTryLock(pw_sync_Mutex* mutex) {
+ return pw_sync_Mutex_TryLock(mutex);
+}
+
+bool pw_sync_Mutex_CallTryLockFor(
+ pw_sync_Mutex* mutex, pw_chrono_SystemClock_TickCount for_at_least) {
+ return pw_sync_Mutex_TryLockFor(mutex, for_at_least);
+}
+
+bool pw_sync_Mutex_CallTryLockUntil(
+ pw_sync_Mutex* mutex, pw_chrono_SystemClock_TimePoint until_at_least) {
+ return pw_sync_Mutex_TryLockUntil(mutex, until_at_least);
+}
+
+void pw_sync_Mutex_CallUnlock(pw_sync_Mutex* mutex) {
+ pw_sync_Mutex_Unlock(mutex);
+}
diff --git a/pw_sync/public/pw_sync/binary_semaphore.h b/pw_sync/public/pw_sync/binary_semaphore.h
new file mode 100644
index 0000000..53925d3
--- /dev/null
+++ b/pw_sync/public/pw_sync/binary_semaphore.h
@@ -0,0 +1,116 @@
+// Copyright 2020 The Pigweed Authors
+//
+// 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.
+#pragma once
+
+#include <stddef.h>
+
+#include "pw_chrono/system_clock.h"
+#include "pw_preprocessor/util.h"
+
+#ifdef __cplusplus
+
+#include "pw_sync_backend/binary_semaphore_native.h"
+
+namespace pw::sync {
+
+// BinarySemaphore is a specialization of CountingSemaphore with arbitrary
+// token limit of 1. Note that that max() is >= 1, meaning it may be
+// released up to max() times but only acquired once for those N releases.
+// Implementations of BinarySemaphore are typically more efficient than the
+// default implementation of CountingSemaphore. The entire API is thread safe
+// but only a subset is IRQ safe.
+//
+// WARNING: In order to support global statically constructed BinarySemaphores,
+// the backend MUST ensure that any initialization required in your environment
+// prior to the creation and/or initialization of the native semaphore
+// (e.g. kernel initialization), is done before or during the invocation of the
+// global static C++ constructors.
+class BinarySemaphore {
+ public:
+ using native_handle_type = backend::NativeBinarySemaphoreHandle;
+
+ BinarySemaphore();
+ ~BinarySemaphore();
+ BinarySemaphore(const BinarySemaphore&) = delete;
+ BinarySemaphore(BinarySemaphore&&) = delete;
+ BinarySemaphore& operator=(const BinarySemaphore&) = delete;
+ BinarySemaphore& operator=(BinarySemaphore&&) = delete;
+
+ // Atomically increments the internal counter by 1 up to max_count.
+ // Any thread(s) waiting for the counter to be greater than 0,
+ // such as due to being blocked in acquire, will subsequently be unblocked.
+ // This is IRQ safe.
+ //
+ // PRECONDITIONS:
+ // 1 <= max() - counter
+ void release();
+
+ // Decrements the internal counter to 0 or blocks indefinitely until it can.
+ // This is thread safe.
+
+ // update <= max() - counter
+ void acquire();
+
+ // Attempts to decrement by the internal counter to 0 without blocking.
+ // Returns true if the internal counter was reset successfully.
+ // This is IRQ safe.
+ bool try_acquire();
+
+ // Attempts to decrement the internal counter to 0 where, if needed, blocking
+ // for at least the specified duration.
+ // Returns true if the internal counter was decremented successfully.
+ // This is thread safe.
+ bool try_acquire_for(chrono::SystemClock::duration for_at_least);
+
+ // Attempts to decrement the internal counter to 0 where, if needed, blocking
+ // until at least the specified time point.
+ // Returns true if the internal counter was decremented successfully.
+ // This is thread safe.
+ bool try_acquire_until(chrono::SystemClock::time_point until_at_least);
+
+ static constexpr ptrdiff_t max() { return backend::kBinarySemaphoreMaxValue; }
+
+ native_handle_type native_handle();
+
+ private:
+ // This may be a wrapper around a native type with additional members.
+ backend::NativeBinarySemaphore native_type_;
+};
+
+} // namespace pw::sync
+
+#include "pw_sync_backend/binary_semaphore_inline.h"
+
+using pw_sync_BinarySemaphore = pw::sync::BinarySemaphore;
+
+#else // !defined(__cplusplus)
+
+typedef struct pw_sync_BinarySemaphore pw_sync_BinarySemaphore;
+
+#endif // __cplusplus
+
+PW_EXTERN_C_START
+
+void pw_sync_BinarySemaphore_Release(pw_sync_BinarySemaphore* semaphore);
+void pw_sync_BinarySemaphore_Acquire(pw_sync_BinarySemaphore* semaphore);
+bool pw_sync_BinarySemaphore_TryAcquire(pw_sync_BinarySemaphore* semaphore);
+bool pw_sync_BinarySemaphore_TryAcquireFor(
+ pw_sync_BinarySemaphore* semaphore,
+ pw_chrono_SystemClock_TickCount for_at_least);
+bool pw_sync_BinarySemaphore_TryAcquireUntil(
+ pw_sync_BinarySemaphore* semaphore,
+ pw_chrono_SystemClock_TimePoint until_at_least);
+ptrdiff_t pw_sync_BinarySemaphore_Max(void);
+
+PW_EXTERN_C_END
diff --git a/pw_sync/public/pw_sync/counting_semaphore.h b/pw_sync/public/pw_sync/counting_semaphore.h
new file mode 100644
index 0000000..18dee02
--- /dev/null
+++ b/pw_sync/public/pw_sync/counting_semaphore.h
@@ -0,0 +1,120 @@
+// Copyright 2020 The Pigweed Authors
+//
+// 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.
+#pragma once
+
+#include <stddef.h>
+
+#include "pw_chrono/system_clock.h"
+#include "pw_preprocessor/util.h"
+
+#ifdef __cplusplus
+
+#include "pw_sync_backend/counting_semaphore_native.h"
+
+namespace pw::sync {
+
+// The CountingSemaphore is a synchronization primitive that can be used for
+// counting events and/or resource management where receiver(s) can block on
+// acquire until notifier(s) signal by invoking release.
+// Note that unlike Mutexes, priority inheritance is not used by semaphores
+// meaning semaphores are subject to unbounded priority inversions.
+// Pigweed does not recommend semaphores for mutual exclusion. The entire API is
+// thread safe but only a subset is IRQ safe.
+//
+// WARNING: In order to support global statically constructed
+// CountingSemaphores, the backend MUST ensure that any initialization required
+// in your environment prior to the creation and/or initialization of the native
+// semaphore (e.g. kernel initialization), is done before or during the
+// invocation of the global static C++ constructors.
+class CountingSemaphore {
+ public:
+ using native_handle_type = backend::NativeCountingSemaphoreHandle;
+
+ CountingSemaphore();
+ ~CountingSemaphore();
+ CountingSemaphore(const CountingSemaphore&) = delete;
+ CountingSemaphore(CountingSemaphore&&) = delete;
+ CountingSemaphore& operator=(const CountingSemaphore&) = delete;
+ CountingSemaphore& operator=(CountingSemaphore&&) = delete;
+
+ // Atomically increments the internal counter by the value of update.
+ // Any thread(s) waiting for the counter to be greater than 0, i.e.
+ // blocked in acquire, will subsequently be unblocked.
+ // This is IRQ safe.
+ //
+ // PRECONDITIONS:
+ // update >= 0
+ // update <= max() - counter
+ void release(ptrdiff_t update = 1);
+
+ // Decrements the internal counter by 1 or blocks indefinitely until it can.
+ // This is thread safe.
+ void acquire();
+
+ // Attempts to decrement by the internal counter by 1 without blocking.
+ // Returns true if the internal counter was decremented successfully.
+ // This is IRQ safe.
+ bool try_acquire();
+
+ // Attempts to decrement the internal counter by 1 where, if needed, blocking
+ // for at least the specified duration.
+ // Returns true if the internal counter was decremented successfully.
+ // This is thread safe.
+ bool try_acquire_for(chrono::SystemClock::duration for_at_least);
+
+ // Attempts to decrement the internal counter by 1 where, if needed, blocking
+ // until at least the specified time point.
+ // Returns true if the internal counter was decremented successfully.
+ // This is thread safe.
+ bool try_acquire_until(chrono::SystemClock::time_point until_at_least);
+
+ static constexpr ptrdiff_t max() {
+ return backend::kCountingSemaphoreMaxValue;
+ }
+
+ native_handle_type native_handle();
+
+ private:
+ // This may be a wrapper around a native type with additional members.
+ backend::NativeCountingSemaphore native_type_;
+};
+
+} // namespace pw::sync
+
+#include "pw_sync_backend/counting_semaphore_inline.h"
+
+using pw_sync_CountingSemaphore = pw::sync::CountingSemaphore;
+
+#else // !defined(__cplusplus)
+
+typedef struct pw_sync_CountingSemaphore pw_sync_CountingSemaphore;
+
+#endif // __cplusplus
+
+PW_EXTERN_C_START
+
+void pw_sync_CountingSemaphore_Release(pw_sync_CountingSemaphore* semaphore);
+void pw_sync_CountingSemaphore_ReleaseNum(pw_sync_CountingSemaphore* semaphore,
+ ptrdiff_t update);
+void pw_sync_CountingSemaphore_Acquire(pw_sync_CountingSemaphore* semaphore);
+bool pw_sync_CountingSemaphore_TryAcquire(pw_sync_CountingSemaphore* semaphore);
+bool pw_sync_CountingSemaphore_TryAcquireFor(
+ pw_sync_CountingSemaphore* semaphore,
+ pw_chrono_SystemClock_TickCount for_at_least);
+bool pw_sync_CountingSemaphore_TryAcquireUntil(
+ pw_sync_CountingSemaphore* semaphore,
+ pw_chrono_SystemClock_TimePoint until_at_least);
+ptrdiff_t pw_sync_CountingSemaphore_Max(void);
+
+PW_EXTERN_C_END
diff --git a/pw_sync/public/pw_sync/mutex.h b/pw_sync/public/pw_sync/mutex.h
new file mode 100644
index 0000000..aef10e3
--- /dev/null
+++ b/pw_sync/public/pw_sync/mutex.h
@@ -0,0 +1,98 @@
+// Copyright 2020 The Pigweed Authors
+//
+// 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.
+#pragma once
+
+#include <stdbool.h>
+
+#include "pw_chrono/system_clock.h"
+#include "pw_preprocessor/util.h"
+
+#ifdef __cplusplus
+
+#include "pw_sync_backend/mutex_native.h"
+
+namespace pw::sync {
+
+// The Mutex is a synchronization primitive that can be used to protect
+// shared data from being simultaneously accessed by multiple threads.
+// It offers exclusive, non-recursive ownership semantics where priority
+// inheritance is used to solve the classic priority-inversion problem.
+// This is thread safe, but NOT IRQ safe.
+//
+// WARNING: In order to support global statically constructed Mutex, the backend
+// MUST ensure that any initialization required in your environment prior to the
+// creation and/or initialization of the native semaphore (e.g. kernel
+// initialization), is done before or during the invocation of the global static
+// C++ constructors.
+class Mutex {
+ public:
+ using native_handle_type = backend::NativeMutexHandle;
+
+ Mutex();
+ ~Mutex();
+ Mutex(const Mutex&) = delete;
+ Mutex(Mutex&&) = delete;
+ Mutex& operator=(const Mutex&) = delete;
+ Mutex& operator=(Mutex&&) = delete;
+
+ // Locks the mutex, blocking indefinitely. Failures are fatal.
+ void lock();
+
+ // Attempts to lock the mutex in a non-blocking manner.
+ // Returns true if the mutex was successfully acquired.
+ bool try_lock();
+
+ // Attempts to lock the mutex where, if needed, blocking for at least the
+ // specified duration.
+ // Returns true if the mutex was successfully acquired.
+ bool try_lock_for(chrono::SystemClock::duration for_at_least);
+
+ // Attempts to lock the mutex where, if needed, blocking until at least the
+ // specified time_point.
+ // Returns true if the mutex was successfully acquired.
+ bool try_lock_until(chrono::SystemClock::time_point until_at_least);
+
+ // Unlocks the mutex. Failures are fatal.
+ void unlock();
+
+ native_handle_type native_handle();
+
+ private:
+ // This may be a wrapper around a native type with additional members.
+ backend::NativeMutex native_type_;
+};
+
+} // namespace pw::sync
+
+#include "pw_sync_backend/mutex_inline.h"
+
+using pw_sync_Mutex = pw::sync::Mutex;
+
+#else // !defined(__cplusplus)
+
+typedef struct pw_sync_Mutex pw_sync_Mutex;
+
+#endif // __cplusplus
+
+PW_EXTERN_C_START
+
+void pw_sync_Mutex_Lock(pw_sync_Mutex* mutex);
+bool pw_sync_Mutex_TryLock(pw_sync_Mutex* mutex);
+bool pw_sync_Mutex_TryLockFor(pw_sync_Mutex* mutex,
+ pw_chrono_SystemClock_TickCount for_at_least);
+bool pw_sync_Mutex_TryLockUntil(pw_sync_Mutex* mutex,
+ pw_chrono_SystemClock_TimePoint until_at_least);
+void pw_sync_Mutex_Unlock(pw_sync_Mutex* mutex);
+
+PW_EXTERN_C_END
diff --git a/pw_sync/spin_lock_facade_test.cc b/pw_sync/spin_lock_facade_test.cc
index c115bd9..a373e79 100644
--- a/pw_sync/spin_lock_facade_test.cc
+++ b/pw_sync/spin_lock_facade_test.cc
@@ -33,6 +33,8 @@
spin_lock.unlock();
}
+// TODO(pwbug/291): Add real concurrency tests once we have pw::thread.
+
SpinLock static_spin_lock;
TEST(SpinLock, LockUnlockStatic) {
static_spin_lock.lock();
diff --git a/pw_sync_stl/BUILD b/pw_sync_stl/BUILD
index 13b6d64..97dc246 100644
--- a/pw_sync_stl/BUILD
+++ b/pw_sync_stl/BUILD
@@ -22,6 +22,90 @@
licenses(["notice"]) # Apache License 2.0
pw_cc_library(
+ name = "binary_semaphore_headers",
+ hdrs = [
+ "public/pw_sync_stl/binary_semaphore_inline.h",
+ "public/pw_sync_stl/binary_semaphore_native.h",
+ "public_overrides/pw_sync_backend/binary_semaphore_inline.h",
+ "public_overrides/pw_sync_backend/binary_semaphore_native.h",
+ ],
+ includes = [
+ "public",
+ "public_overrides",
+ ],
+ deps = [
+ "//pw_chrono:system_clock",
+ ],
+)
+
+pw_cc_library(
+ name = "binary_semaphore",
+ srcs = [
+ "binary_semaphore.cc",
+ ],
+ deps = [
+ ":binary_semaphore_headers",
+ "//pw_chrono:system_clock",
+ "//pw_sync:binary_semaphore_facade",
+ ],
+)
+
+pw_cc_library(
+ name = "counting_semaphore_headers",
+ hdrs = [
+ "public/pw_sync_stl/counting_semaphore_inline.h",
+ "public/pw_sync_stl/counting_semaphore_native.h",
+ "public_overrides/pw_sync_backend/counting_semaphore_inline.h",
+ "public_overrides/pw_sync_backend/counting_semaphore_native.h",
+ ],
+ includes = [
+ "public",
+ "public_overrides",
+ ],
+ deps = [
+ "//pw_chrono:system_clock",
+ ],
+)
+
+pw_cc_library(
+ name = "counting_semaphore",
+ srcs = [
+ "counting_semaphore.cc",
+ ],
+ deps = [
+ ":counting_semaphore_headers",
+ "//pw_chrono:system_clock",
+ "//pw_sync:counting_semaphore_facade",
+ ],
+)
+
+pw_cc_library(
+ name = "mutex_headers",
+ hdrs = [
+ "public/pw_sync_stl/mutex_inline.h",
+ "public/pw_sync_stl/mutex_native.h",
+ "public_overrides/pw_sync_backend/mutex_inline.h",
+ "public_overrides/pw_sync_backend/mutex_native.h",
+ ],
+ includes = [
+ "public",
+ "public_overrides",
+ ],
+ deps = [
+ "//pw_chrono:system_clock",
+ ],
+)
+
+pw_cc_library(
+ name = "mutex",
+ deps = [
+ ":mutex_headers",
+ "//pw_chrono:system_clock",
+ "//pw_sync:mutex_facade",
+ ],
+)
+
+pw_cc_library(
name = "spin_lock_headers",
hdrs = [
"public/pw_sync_stl/spin_lock_inline.h",
diff --git a/pw_sync_stl/BUILD.gn b/pw_sync_stl/BUILD.gn
index eacd5dd..f3fdebb 100644
--- a/pw_sync_stl/BUILD.gn
+++ b/pw_sync_stl/BUILD.gn
@@ -15,6 +15,7 @@
import("//build_overrides/pigweed.gni")
import("$dir_pw_build/target_types.gni")
+import("$dir_pw_chrono/backend.gni")
import("$dir_pw_docgen/docs.gni")
config("public_include_path") {
@@ -27,6 +28,79 @@
visibility = [ ":*" ]
}
+# This target provides the backend for pw::sync::BinarySemaphore.
+pw_source_set("binary_semaphore_backend") {
+ public_configs = [
+ ":public_include_path",
+ ":backend_config",
+ ]
+ public = [
+ "public/pw_sync_stl/binary_semaphore_inline.h",
+ "public/pw_sync_stl/binary_semaphore_native.h",
+ "public_overrides/pw_sync_backend/binary_semaphore_inline.h",
+ "public_overrides/pw_sync_backend/binary_semaphore_native.h",
+ ]
+ sources = [ "binary_semaphore.cc" ]
+ deps = [
+ "$dir_pw_assert",
+ "$dir_pw_chrono:system_clock",
+ "$dir_pw_sync:binary_semaphore.facade",
+ ]
+ assert(
+ pw_chrono_SYSTEM_CLOCK_BACKEND == "" ||
+ pw_chrono_SYSTEM_CLOCK_BACKEND == "$dir_pw_chrono_stl:system_clock",
+ "The STL pw::sync::BinarySemaphore backend only works with the " +
+ "STL pw::chrono::SystemClock backend.")
+}
+
+# This target provides the backend for pw::sync::CountingSemaphore.
+pw_source_set("counting_semaphore_backend") {
+ public_configs = [
+ ":public_include_path",
+ ":backend_config",
+ ]
+ public = [
+ "public/pw_sync_stl/counting_semaphore_inline.h",
+ "public/pw_sync_stl/counting_semaphore_native.h",
+ "public_overrides/pw_sync_backend/counting_semaphore_inline.h",
+ "public_overrides/pw_sync_backend/counting_semaphore_native.h",
+ ]
+ sources = [ "counting_semaphore.cc" ]
+ deps = [
+ "$dir_pw_assert",
+ "$dir_pw_chrono:system_clock",
+ "$dir_pw_sync:counting_semaphore.facade",
+ ]
+ assert(
+ pw_chrono_SYSTEM_CLOCK_BACKEND == "" ||
+ pw_chrono_SYSTEM_CLOCK_BACKEND == "$dir_pw_chrono_stl:system_clock",
+ "The STL pw::sync::CountingSemaphore backend only works with the " +
+ "STL pw::chrono::SystemClock backend.")
+}
+
+# This target provides the backend for pw::sync::Mutex.
+pw_source_set("mutex_backend") {
+ public_configs = [
+ ":public_include_path",
+ ":backend_config",
+ ]
+ public = [
+ "public/pw_sync_stl/mutex_inline.h",
+ "public/pw_sync_stl/mutex_native.h",
+ "public_overrides/pw_sync_backend/mutex_inline.h",
+ "public_overrides/pw_sync_backend/mutex_native.h",
+ ]
+ deps = [
+ "$dir_pw_chrono:system_clock",
+ "$dir_pw_sync:mutex.facade",
+ ]
+ assert(
+ pw_chrono_SYSTEM_CLOCK_BACKEND == "" ||
+ pw_chrono_SYSTEM_CLOCK_BACKEND == "$dir_pw_chrono_stl:system_clock",
+ "The STL pw::sync::Mutex backend only works with the STL " +
+ "pw::chrono::SystemClock backend.")
+}
+
# This target provides the backend for pw::sync::SpinLock.
pw_source_set("spin_lock_backend") {
public_configs = [
diff --git a/pw_sync_stl/binary_semaphore.cc b/pw_sync_stl/binary_semaphore.cc
new file mode 100644
index 0000000..f1f3fb4
--- /dev/null
+++ b/pw_sync_stl/binary_semaphore.cc
@@ -0,0 +1,56 @@
+// Copyright 2020 The Pigweed Authors
+//
+// 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 "pw_sync/binary_semaphore.h"
+
+#include "pw_assert/assert.h"
+
+using pw::chrono::SystemClock;
+
+namespace pw::sync {
+
+void BinarySemaphore::release() {
+ std::lock_guard lock(native_type_.mutex);
+ PW_DCHECK_UINT_LT(native_type_.count, BinarySemaphore::max());
+ ++native_type_.count;
+ native_type_.condition.notify_one();
+}
+
+void BinarySemaphore::acquire() {
+ std::unique_lock lock(native_type_.mutex);
+ native_type_.condition.wait(lock, [&] { return native_type_.count != 0; });
+ native_type_.count = 0;
+}
+
+bool BinarySemaphore::try_acquire() {
+ std::lock_guard lock(native_type_.mutex);
+ if (native_type_.count != 0) {
+ native_type_.count = 0;
+ return true;
+ }
+ return false;
+}
+
+bool BinarySemaphore::try_acquire_until(
+ SystemClock::time_point until_at_least) {
+ std::unique_lock lock(native_type_.mutex);
+ if (native_type_.condition.wait_until(
+ lock, until_at_least, [&] { return native_type_.count != 0; })) {
+ native_type_.count = 0;
+ return true;
+ }
+ return false;
+}
+
+} // namespace pw::sync
diff --git a/pw_sync_stl/counting_semaphore.cc b/pw_sync_stl/counting_semaphore.cc
new file mode 100644
index 0000000..781aa77
--- /dev/null
+++ b/pw_sync_stl/counting_semaphore.cc
@@ -0,0 +1,59 @@
+// Copyright 2020 The Pigweed Authors
+//
+// 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 "pw_sync/counting_semaphore.h"
+
+#include "pw_assert/assert.h"
+
+using pw::chrono::SystemClock;
+
+namespace pw::sync {
+
+void CountingSemaphore::release(ptrdiff_t update) {
+ PW_DCHECK_UINT_GE(update, 0);
+ {
+ std::lock_guard lock(native_type_.mutex);
+ PW_DCHECK_UINT_LE(update, CountingSemaphore::max() - native_type_.count);
+ native_type_.count += update;
+ native_type_.condition.notify_one();
+ }
+}
+
+void CountingSemaphore::acquire() {
+ std::unique_lock lock(native_type_.mutex);
+ native_type_.condition.wait(lock, [&] { return native_type_.count != 0; });
+ --native_type_.count;
+}
+
+bool CountingSemaphore::try_acquire() {
+ std::lock_guard lock(native_type_.mutex);
+ if (native_type_.count != 0) {
+ --native_type_.count;
+ return true;
+ }
+ return false;
+}
+
+bool CountingSemaphore::try_acquire_until(
+ SystemClock::time_point until_at_least) {
+ std::unique_lock lock(native_type_.mutex);
+ if (native_type_.condition.wait_until(
+ lock, until_at_least, [&] { return native_type_.count != 0; })) {
+ --native_type_.count;
+ return true;
+ }
+ return false;
+}
+
+} // namespace pw::sync
diff --git a/pw_sync_stl/public/pw_sync_stl/binary_semaphore_inline.h b/pw_sync_stl/public/pw_sync_stl/binary_semaphore_inline.h
new file mode 100644
index 0000000..ca5a1e3
--- /dev/null
+++ b/pw_sync_stl/public/pw_sync_stl/binary_semaphore_inline.h
@@ -0,0 +1,41 @@
+// Copyright 2020 The Pigweed Authors
+//
+// 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.
+#pragma once
+
+#include "pw_chrono/system_clock.h"
+#include "pw_sync/binary_semaphore.h"
+
+namespace pw::sync {
+
+inline BinarySemaphore::BinarySemaphore()
+ : native_type_{.mutex = {}, .condition = {}, .count = 0} {}
+
+inline BinarySemaphore::~BinarySemaphore() {}
+
+inline bool BinarySemaphore::try_acquire_for(
+ chrono::SystemClock::duration for_at_least) {
+ // Due to spurious condition variable wakeups we prefer not to use wait_for()
+ // as we may grossly extend the effective deadline after a spruious wakeup.
+ // Ergo we instead use the derived deadline which can be re-used many times
+ // without shifting the effective deadline. For more details on spurious
+ // wakeups and CVs on Windows and POSIX see:
+ // https://en.wikipedia.org/wiki/Spurious_wakeup
+ return try_acquire_until(chrono::SystemClock::now() + for_at_least);
+}
+
+inline BinarySemaphore::native_handle_type BinarySemaphore::native_handle() {
+ return native_type_;
+}
+
+} // namespace pw::sync
diff --git a/pw_sync_stl/public/pw_sync_stl/binary_semaphore_native.h b/pw_sync_stl/public/pw_sync_stl/binary_semaphore_native.h
new file mode 100644
index 0000000..f19a2cc4
--- /dev/null
+++ b/pw_sync_stl/public/pw_sync_stl/binary_semaphore_native.h
@@ -0,0 +1,32 @@
+// Copyright 2020 The Pigweed Authors
+//
+// 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.
+#pragma once
+
+#include <condition_variable>
+#include <limits>
+#include <mutex>
+
+namespace pw::sync::backend {
+
+struct NativeBinarySemaphore {
+ std::mutex mutex;
+ std::condition_variable_any condition;
+ ptrdiff_t count;
+};
+using NativeBinarySemaphoreHandle = NativeBinarySemaphore&;
+
+inline constexpr ptrdiff_t kBinarySemaphoreMaxValue =
+ std::numeric_limits<ptrdiff_t>::max();
+
+} // namespace pw::sync::backend
diff --git a/pw_sync_stl/public/pw_sync_stl/counting_semaphore_inline.h b/pw_sync_stl/public/pw_sync_stl/counting_semaphore_inline.h
new file mode 100644
index 0000000..f29df4b
--- /dev/null
+++ b/pw_sync_stl/public/pw_sync_stl/counting_semaphore_inline.h
@@ -0,0 +1,39 @@
+// Copyright 2020 The Pigweed Authors
+//
+// 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.
+#pragma once
+
+#include "pw_chrono/system_clock.h"
+#include "pw_sync/counting_semaphore.h"
+
+using pw::chrono::SystemClock;
+
+namespace pw::sync {
+
+inline CountingSemaphore::CountingSemaphore()
+ : native_type_{.mutex = {}, .condition = {}, .count = 0} {}
+
+inline CountingSemaphore::~CountingSemaphore() {}
+
+inline bool CountingSemaphore::try_acquire_for(
+ chrono::SystemClock::duration for_at_least) {
+ // Due to spurious condition variable wakeups this cannot use wait_for().
+ return try_acquire_until(chrono::SystemClock::now() + for_at_least);
+}
+
+inline CountingSemaphore::native_handle_type
+CountingSemaphore::native_handle() {
+ return native_type_;
+}
+
+} // namespace pw::sync
diff --git a/pw_sync_stl/public/pw_sync_stl/counting_semaphore_native.h b/pw_sync_stl/public/pw_sync_stl/counting_semaphore_native.h
new file mode 100644
index 0000000..8b06307
--- /dev/null
+++ b/pw_sync_stl/public/pw_sync_stl/counting_semaphore_native.h
@@ -0,0 +1,32 @@
+// Copyright 2020 The Pigweed Authors
+//
+// 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.
+#pragma once
+
+#include <condition_variable>
+#include <limits>
+#include <mutex>
+
+namespace pw::sync::backend {
+
+struct NativeCountingSemaphore {
+ std::mutex mutex;
+ std::condition_variable_any condition;
+ ptrdiff_t count;
+};
+using NativeCountingSemaphoreHandle = NativeCountingSemaphore&;
+
+inline constexpr ptrdiff_t kCountingSemaphoreMaxValue =
+ std::numeric_limits<ptrdiff_t>::max();
+
+} // namespace pw::sync::backend
diff --git a/pw_sync_stl/public/pw_sync_stl/mutex_inline.h b/pw_sync_stl/public/pw_sync_stl/mutex_inline.h
new file mode 100644
index 0000000..b4459eb
--- /dev/null
+++ b/pw_sync_stl/public/pw_sync_stl/mutex_inline.h
@@ -0,0 +1,42 @@
+// Copyright 2020 The Pigweed Authors
+//
+// 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.
+#pragma once
+
+#include "pw_chrono/system_clock.h"
+#include "pw_sync/mutex.h"
+
+namespace pw::sync {
+
+inline Mutex::Mutex() : native_type_() {}
+
+inline Mutex::~Mutex() {}
+
+inline void Mutex::lock() { native_type_.lock(); }
+
+inline bool Mutex::try_lock() { return native_type_.try_lock(); }
+
+inline bool Mutex::try_lock_for(chrono::SystemClock::duration for_at_least) {
+ return native_type_.try_lock_for(for_at_least);
+}
+
+inline bool Mutex::try_lock_until(
+ chrono::SystemClock::time_point until_at_least) {
+ return native_type_.try_lock_until(until_at_least);
+}
+
+inline void Mutex::unlock() { native_type_.unlock(); }
+
+inline Mutex::native_handle_type Mutex::native_handle() { return native_type_; }
+
+} // namespace pw::sync
diff --git a/pw_sync_stl/public/pw_sync_stl/mutex_native.h b/pw_sync_stl/public/pw_sync_stl/mutex_native.h
new file mode 100644
index 0000000..fb04d56
--- /dev/null
+++ b/pw_sync_stl/public/pw_sync_stl/mutex_native.h
@@ -0,0 +1,23 @@
+// Copyright 2020 The Pigweed Authors
+//
+// 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.
+#pragma once
+
+#include <mutex>
+
+namespace pw::sync::backend {
+
+using NativeMutex = std::timed_mutex;
+using NativeMutexHandle = std::timed_mutex&;
+
+} // namespace pw::sync::backend
diff --git a/pw_sync_stl/public_overrides/pw_sync_backend/binary_semaphore_inline.h b/pw_sync_stl/public_overrides/pw_sync_backend/binary_semaphore_inline.h
new file mode 100644
index 0000000..96d102c
--- /dev/null
+++ b/pw_sync_stl/public_overrides/pw_sync_backend/binary_semaphore_inline.h
@@ -0,0 +1,16 @@
+// Copyright 2020 The Pigweed Authors
+//
+// 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.
+#pragma once
+
+#include "pw_sync_stl/binary_semaphore_inline.h"
diff --git a/pw_sync_stl/public_overrides/pw_sync_backend/binary_semaphore_native.h b/pw_sync_stl/public_overrides/pw_sync_backend/binary_semaphore_native.h
new file mode 100644
index 0000000..f855f73
--- /dev/null
+++ b/pw_sync_stl/public_overrides/pw_sync_backend/binary_semaphore_native.h
@@ -0,0 +1,16 @@
+// Copyright 2020 The Pigweed Authors
+//
+// 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.
+#pragma once
+
+#include "pw_sync_stl/binary_semaphore_native.h"
diff --git a/pw_sync_stl/public_overrides/pw_sync_backend/counting_semaphore_inline.h b/pw_sync_stl/public_overrides/pw_sync_backend/counting_semaphore_inline.h
new file mode 100644
index 0000000..99ad224
--- /dev/null
+++ b/pw_sync_stl/public_overrides/pw_sync_backend/counting_semaphore_inline.h
@@ -0,0 +1,16 @@
+// Copyright 2020 The Pigweed Authors
+//
+// 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.
+#pragma once
+
+#include "pw_sync_stl/counting_semaphore_inline.h"
diff --git a/pw_sync_stl/public_overrides/pw_sync_backend/counting_semaphore_native.h b/pw_sync_stl/public_overrides/pw_sync_backend/counting_semaphore_native.h
new file mode 100644
index 0000000..041856b
--- /dev/null
+++ b/pw_sync_stl/public_overrides/pw_sync_backend/counting_semaphore_native.h
@@ -0,0 +1,16 @@
+// Copyright 2020 The Pigweed Authors
+//
+// 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.
+#pragma once
+
+#include "pw_sync_stl/counting_semaphore_native.h"
diff --git a/pw_sync_stl/public_overrides/pw_sync_backend/mutex_inline.h b/pw_sync_stl/public_overrides/pw_sync_backend/mutex_inline.h
new file mode 100644
index 0000000..5c48c56
--- /dev/null
+++ b/pw_sync_stl/public_overrides/pw_sync_backend/mutex_inline.h
@@ -0,0 +1,16 @@
+// Copyright 2020 The Pigweed Authors
+//
+// 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.
+#pragma once
+
+#include "pw_sync_stl/mutex_inline.h"
diff --git a/pw_sync_stl/public_overrides/pw_sync_backend/mutex_native.h b/pw_sync_stl/public_overrides/pw_sync_backend/mutex_native.h
new file mode 100644
index 0000000..d37bd48
--- /dev/null
+++ b/pw_sync_stl/public_overrides/pw_sync_backend/mutex_native.h
@@ -0,0 +1,16 @@
+// Copyright 2020 The Pigweed Authors
+//
+// 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.
+#pragma once
+
+#include "pw_sync_stl/mutex_native.h"
diff --git a/pw_sync_stl/public_overrides/pw_sync_backend/spin_lock_inline.h b/pw_sync_stl/public_overrides/pw_sync_backend/spin_lock_inline.h
index 0307f88..57c17c2 100644
--- a/pw_sync_stl/public_overrides/pw_sync_backend/spin_lock_inline.h
+++ b/pw_sync_stl/public_overrides/pw_sync_backend/spin_lock_inline.h
@@ -11,9 +11,6 @@
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
-
-// This override header includes the main tokenized logging header and defines
-// the PW_LOG macro as the tokenized logging macro.
#pragma once
#include "pw_sync_stl/spin_lock_inline.h"
diff --git a/pw_sync_stl/public_overrides/pw_sync_backend/spin_lock_native.h b/pw_sync_stl/public_overrides/pw_sync_backend/spin_lock_native.h
index 9d1922b..41ff621 100644
--- a/pw_sync_stl/public_overrides/pw_sync_backend/spin_lock_native.h
+++ b/pw_sync_stl/public_overrides/pw_sync_backend/spin_lock_native.h
@@ -11,9 +11,6 @@
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
-
-// This override header includes the main tokenized logging header and defines
-// the PW_LOG macro as the tokenized logging macro.
#pragma once
#include "pw_sync_stl/spin_lock_native.h"
diff --git a/targets/host/target_toolchains.gni b/targets/host/target_toolchains.gni
index c757915..cd89d8f 100644
--- a/targets/host/target_toolchains.gni
+++ b/targets/host/target_toolchains.gni
@@ -69,11 +69,22 @@
pw_unit_test_AUTOMATIC_RUNNER = get_path_info("run_test.bat", "abspath")
}
+# TODO(amontanez): figure out why std::mutex doesn't work on Windows.
+# These current target configurations do not work on windows.
+_win_incompatible_config = {
+ pw_sync_BINARY_SEMAPHORE_BACKEND = "$dir_pw_sync_stl:binary_semaphore_backend"
+ pw_sync_COUNTING_SEMAPHORE_BACKEND =
+ "$dir_pw_sync_stl:counting_semaphore_backend"
+ pw_sync_MUTEX_BACKEND = "$dir_pw_sync_stl:mutex_backend"
+}
+
_os_specific_config = {
if (host_os == "linux") {
forward_variables_from(_linux_config, "*")
+ forward_variables_from(_win_incompatible_config, "*")
} else if (host_os == "mac") {
forward_variables_from(_mac_config, "*")
+ forward_variables_from(_win_incompatible_config, "*")
} else if (host_os == "win") {
forward_variables_from(_win_config, "*")
}