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/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