pw_thread: adds the initial pw_thread module

Adds a std::this_thread like API through the pw_thread facades, that
is:
1) this_thread::yield
2) this_thread::sleep_{for,until}
3) this_thread::get_id

This module is split into many different facades in order to let users
decide what functionality they want to use as they may not always
be available, for example when using opaque SDKs.

In addition this provides an initial set of backends based on using
the STL's std::this_thread directly and selects them for the host
target.

Change-Id: I0ee8e4390ba988b2b13e9ee59f976f2333715f1f
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/30040
Commit-Queue: Ewout van Bekkum <ewout@google.com>
Reviewed-by: Keir Mierle <keir@google.com>
Reviewed-by: Wyatt Hepler <hepler@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index 246c668..cb7f78c 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -234,6 +234,7 @@
       "$dir_pw_string",
       "$dir_pw_sync",
       "$dir_pw_sys_io",
+      "$dir_pw_thread",
       "$dir_pw_trace",
       "$dir_pw_unit_test",
       "$dir_pw_varint",
@@ -288,6 +289,7 @@
       "$dir_pw_stream:tests",
       "$dir_pw_string:tests",
       "$dir_pw_sync:tests",
+      "$dir_pw_thread:tests",
       "$dir_pw_tokenizer:tests",
       "$dir_pw_trace:tests",
       "$dir_pw_trace_tokenized:tests",
diff --git a/modules.gni b/modules.gni
index a31dfac..67b672f 100644
--- a/modules.gni
+++ b/modules.gni
@@ -90,6 +90,8 @@
   dir_pw_sys_io_arduino = get_path_info("pw_sys_io_arduino", "abspath")
   dir_pw_sys_io_stdio = get_path_info("pw_sys_io_stdio", "abspath")
   dir_pw_target_runner = get_path_info("pw_target_runner", "abspath")
+  dir_pw_thread = get_path_info("pw_thread", "abspath")
+  dir_pw_thread_stl = get_path_info("pw_thread_stl", "abspath")
   dir_pw_third_party = get_path_info("third_party", "abspath")
   dir_pw_tokenizer = get_path_info("pw_tokenizer", "abspath")
   dir_pw_toolchain = get_path_info("pw_toolchain", "abspath")
diff --git a/pw_thread/BUILD b/pw_thread/BUILD
new file mode 100644
index 0000000..4335978
--- /dev/null
+++ b/pw_thread/BUILD
@@ -0,0 +1,153 @@
+# 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.
+
+load(
+    "//pw_build:pigweed.bzl",
+    "pw_cc_library",
+    "pw_cc_test",
+)
+
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"])  # Apache License 2.0
+
+# TODO(pwbug/101): Need to add support for facades/backends to Bazel.
+PW_THREAD_ID_BACKEND = "//pw_thread_stl:id"
+PW_THREAD_SLEEP_BACKEND = "//pw_thread_stl:sleep"
+PW_THREAD_YIELD_BACKEND = "//pw_thread_stl:yield"
+
+pw_cc_library(
+    name = "id_facade",
+    hdrs = [
+        "public/pw_thread/id.h",
+    ],
+    includes = ["public"],
+    deps = [
+        PW_THREAD_ID_BACKEND + "_headers",
+    ],
+)
+
+pw_cc_library(
+    name = "id",
+    deps = [
+        ":id_facade",
+        PW_THREAD_ID_BACKEND + "_headers",
+    ],
+)
+
+pw_cc_library(
+    name = "id_backend",
+    deps = [
+       PW_THREAD_ID_BACKEND,
+    ],
+)
+
+pw_cc_library(
+    name = "sleep_facade",
+    hdrs = [
+        "public/pw_thread/sleep.h",
+    ],
+    includes = ["public"],
+    srcs = [
+        "sleep.cc"
+    ],
+    deps = [
+        PW_THREAD_SLEEP_BACKEND + "_headers",
+        "//pw_chrono:system_clock",
+        "//pw_preprocessor",
+    ],
+)
+
+pw_cc_library(
+    name = "sleep",
+    deps = [
+        ":sleep_facade",
+        PW_THREAD_SLEEP_BACKEND + "_headers",
+    ],
+)
+
+pw_cc_library(
+    name = "sleep_backend",
+    deps = [
+       PW_THREAD_SLEEP_BACKEND,
+    ],
+)
+
+pw_cc_library(
+    name = "yield_facade",
+    hdrs = [
+        "public/pw_thread/yield.h",
+    ],
+    includes = ["public"],
+    srcs = [
+        "yield.cc"
+    ],
+    deps = [
+        PW_THREAD_YIELD_BACKEND + "_headers",
+        "//pw_preprocessor",
+    ],
+)
+
+pw_cc_library(
+    name = "yield",
+    deps = [
+        ":yield_facade",
+        PW_THREAD_YIELD_BACKEND + "_headers",
+    ],
+)
+
+pw_cc_library(
+    name = "yield_backend",
+    deps = [
+       PW_THREAD_YIELD_BACKEND,
+    ],
+)
+
+pw_cc_test(
+    name = "id_facade_test",
+    srcs = [
+        "id_facade_test.cc",
+    ],
+    deps = [
+        ":id",
+        "//pw_unit_test",
+    ],
+)
+
+pw_cc_test(
+    name = "sleep_facade_test",
+    srcs = [
+        "sleep_facade_test.cc",
+        "sleep_facade_test_c.c",
+    ],
+    deps = [
+        ":sleep",
+        "//pw_chrono:system_clock",
+        "//pw_preprocessor",
+        "//pw_unit_test",
+    ],
+)
+
+pw_cc_test(
+    name = "yield_facade_test",
+    srcs = [
+        "yield_facade_test.cc",
+        "yield_facade_test_c.c",
+    ],
+    deps = [
+        ":yield",
+        "//pw_preprocessor",
+        "//pw_unit_test",
+    ],
+)
diff --git a/pw_thread/BUILD.gn b/pw_thread/BUILD.gn
new file mode 100644
index 0000000..436c1a1
--- /dev/null
+++ b/pw_thread/BUILD.gn
@@ -0,0 +1,94 @@
+# 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.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_build/facade.gni")
+import("$dir_pw_build/target_types.gni")
+import("$dir_pw_docgen/docs.gni")
+import("$dir_pw_unit_test/test.gni")
+import("backend.gni")
+
+config("public_include_path") {
+  include_dirs = [ "public" ]
+  visibility = [ ":*" ]
+}
+
+pw_facade("id") {
+  backend = pw_thread_ID_BACKEND
+  public_configs = [ ":public_include_path" ]
+  public = [ "public/pw_thread/id.h" ]
+}
+
+pw_facade("sleep") {
+  backend = pw_thread_SLEEP_BACKEND
+  public_configs = [ ":public_include_path" ]
+  public = [ "public/pw_thread/sleep.h" ]
+  public_deps = [
+    "$dir_pw_chrono:system_clock",
+    "$dir_pw_preprocessor",
+  ]
+  sources = [ "sleep.cc" ]
+}
+
+pw_facade("yield") {
+  backend = pw_thread_YIELD_BACKEND
+  public_configs = [ ":public_include_path" ]
+  public = [ "public/pw_thread/yield.h" ]
+  public_deps = [ "$dir_pw_preprocessor" ]
+  sources = [ "yield.cc" ]
+}
+
+pw_test_group("tests") {
+  tests = [
+    ":id_facade_test",
+    ":sleep_facade_test",
+    ":yield_facade_test",
+  ]
+}
+
+pw_test("id_facade_test") {
+  enable_if = pw_thread_ID_BACKEND != ""
+  sources = [ "id_facade_test.cc" ]
+  deps = [ ":id" ]
+}
+
+pw_test("sleep_facade_test") {
+  enable_if = pw_thread_SLEEP_BACKEND != "" && pw_thread_ID_BACKEND != ""
+  sources = [
+    "sleep_facade_test.cc",
+    "sleep_facade_test_c.c",
+  ]
+  deps = [
+    ":id",
+    ":sleep",
+    "$dir_pw_chrono:system_clock",
+  ]
+}
+
+pw_test("yield_facade_test") {
+  enable_if = pw_thread_YIELD_BACKEND != "" && pw_thread_ID_BACKEND != ""
+  sources = [
+    "yield_facade_test.cc",
+    "yield_facade_test_c.c",
+  ]
+  deps = [
+    ":id",
+    ":yield",
+  ]
+}
+
+pw_doc_group("docs") {
+  sources = [ "docs.rst" ]
+}
diff --git a/pw_thread/backend.gni b/pw_thread/backend.gni
new file mode 100644
index 0000000..b74b899
--- /dev/null
+++ b/pw_thread/backend.gni
@@ -0,0 +1,24 @@
+# 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.
+
+declare_args() {
+  # Backend for the pw_thread module's pw::thread::Id.
+  pw_thread_ID_BACKEND = ""
+
+  # Backend for the pw_thread module's pw::thread::sleep_{for,until}.
+  pw_thread_SLEEP_BACKEND = ""
+
+  # Backend for the pw_thread module's pw::thread::yield.
+  pw_thread_YIELD_BACKEND = ""
+}
diff --git a/pw_thread/docs.rst b/pw_thread/docs.rst
new file mode 100644
index 0000000..af0f8f61
--- /dev/null
+++ b/pw_thread/docs.rst
@@ -0,0 +1,8 @@
+.. _module-pw_thread:
+
+---------
+pw_thread
+---------
+This is a threading module for Pigweed. It is not ready for use, and is under
+construction.
+
diff --git a/pw_thread/id_facade_test.cc b/pw_thread/id_facade_test.cc
new file mode 100644
index 0000000..bb61607
--- /dev/null
+++ b/pw_thread/id_facade_test.cc
@@ -0,0 +1,29 @@
+// 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 "gtest/gtest.h"
+#include "pw_thread/id.h"
+
+namespace pw::this_thread {
+namespace {
+
+TEST(Id, GetId) {
+  // We expect unit tests to run in a thread context.
+  // Unfortunately beyond this we need the ability to create and destroy threads
+  // to test more Id functionality.
+  EXPECT_NE(get_id(), thread::Id());
+}
+
+}  // namespace
+}  // namespace pw::this_thread
diff --git a/pw_thread/public/pw_thread/id.h b/pw_thread/public/pw_thread/id.h
new file mode 100644
index 0000000..ef7c1f0
--- /dev/null
+++ b/pw_thread/public/pw_thread/id.h
@@ -0,0 +1,48 @@
+// 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_thread_backend/id_native.h"
+
+namespace pw::thread {
+
+// The class thread::id is a lightweight, trivially copyable class that serves
+// as a unique identifier of Thread objects.
+//
+// Instances of this class may also hold the special distinct value that does
+// not represent any thread. Once a thread has finished, the value of
+// Thread::id may be reused by another thread.
+//
+// This class is designed for use as key in associative containers, both
+// ordered and unordered.
+//
+// The backend must ensure that:
+// 1) There is a default construct which does not represent a thread.
+// 2) Compare operators (==,!=,<,<=,>,>=) are provided to compare and sort IDs.
+using Id = backend::NativeId;
+
+}  // namespace pw::thread
+
+namespace pw::this_thread {
+
+// This is thread safe, not IRQ safe. It is implementation defined whether this
+// is safe before the scheduler has started.
+thread::Id get_id() noexcept;
+
+}  // namespace pw::this_thread
+
+// The backend can opt to include an inline implementation.
+#if __has_include("pw_thread_backend/id_inline.h")
+#include "pw_thread_backend/id_inline.h"
+#endif  // __has_include("pw_thread_backend/id_inline.h")
diff --git a/pw_thread/public/pw_thread/sleep.h b/pw_thread/public/pw_thread/sleep.h
new file mode 100644
index 0000000..4deb472
--- /dev/null
+++ b/pw_thread/public/pw_thread/sleep.h
@@ -0,0 +1,43 @@
+// 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_preprocessor/util.h"
+
+#ifdef __cplusplus
+
+namespace pw::this_thread {
+
+// This can only be called from a thread, meaning the scheduler is running.
+void sleep_for(chrono::SystemClock::duration for_at_least);
+
+// This can only be called from a thread, meaning the scheduler is running.
+void sleep_until(chrono::SystemClock::time_point until_at_least);
+
+}  // namespace pw::this_thread
+
+// The backend can opt to include inlined implementations.
+#if __has_include("pw_thread_backend/sleep_inline.h")
+#include "pw_thread_backend/sleep_inline.h"
+#endif  // __has_include("pw_thread_backend/sleep_inline.h")
+
+#endif  // __cplusplus
+
+PW_EXTERN_C_START
+
+void pw_this_thread_SleepFor(pw_chrono_SystemClock_TickCount for_at_least);
+void pw_this_thread_SleepUntil(pw_chrono_SystemClock_TimePoint until_at_least);
+
+PW_EXTERN_C_END
diff --git a/pw_thread/public/pw_thread/yield.h b/pw_thread/public/pw_thread/yield.h
new file mode 100644
index 0000000..fc6d89d
--- /dev/null
+++ b/pw_thread/public/pw_thread/yield.h
@@ -0,0 +1,45 @@
+// 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_preprocessor/util.h"
+
+#ifdef __cplusplus
+
+namespace pw::this_thread {
+
+// Provides a hint to the implementation to reschedule the execution of threads,
+// allowing other threads to run.
+//
+// The exact behavior of this function depends on the implementation, in
+// particular on the mechanics of the OS scheduler in use and the state of the
+// system.
+//
+// This can only be called from a thread, meaning the scheduler is running.
+void yield() noexcept;
+
+}  // namespace pw::this_thread
+
+// The backend can opt to include an inline implementation.
+#if __has_include("pw_thread_backend/yield_inline.h")
+#include "pw_thread_backend/yield_inline.h"
+#endif  // __has_include("pw_thread_backend/yield_inline.h")
+
+#endif  // __cplusplus
+
+PW_EXTERN_C_START
+
+void pw_this_thread_Yield(void);
+
+PW_EXTERN_C_END
diff --git a/pw_thread/sleep.cc b/pw_thread/sleep.cc
new file mode 100644
index 0000000..28313fd
--- /dev/null
+++ b/pw_thread/sleep.cc
@@ -0,0 +1,28 @@
+// 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_thread/sleep.h"
+
+using pw::chrono::SystemClock;
+
+extern "C" void pw_this_thread_SleepFor(
+    pw_chrono_SystemClock_TickCount for_at_least) {
+  pw::this_thread::sleep_for(SystemClock::duration(for_at_least));
+}
+
+extern "C" void pw_this_thread_SleepUntil(
+    pw_chrono_SystemClock_TimePoint until_at_least) {
+  pw::this_thread::sleep_until(SystemClock::time_point(
+      SystemClock::duration(until_at_least.ticks_since_epoch)));
+}
diff --git a/pw_thread/sleep_facade_test.cc b/pw_thread/sleep_facade_test.cc
new file mode 100644
index 0000000..5956720
--- /dev/null
+++ b/pw_thread/sleep_facade_test.cc
@@ -0,0 +1,89 @@
+// 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_thread/id.h"
+#include "pw_thread/sleep.h"
+
+using pw::chrono::SystemClock;
+
+namespace pw::this_thread {
+namespace {
+
+extern "C" {
+
+// Functions defined in sleep_facade_test_c.c which call the API from C.
+void pw_this_thread_CallSleepFor(pw_chrono_SystemClock_TickCount for_at_least);
+void pw_this_thread_CallSleepUntil(
+    pw_chrono_SystemClock_TimePoint until_at_least);
+
+}  // 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(Sleep, SleepFor) {
+  // Ensure we are in a thread context, meaning we are permitted to sleep.
+  ASSERT_NE(get_id(), thread::Id());
+
+  const SystemClock::time_point before = SystemClock::now();
+  sleep_for(kRoundedArbitraryDuration);
+  const SystemClock::duration time_elapsed = SystemClock::now() - before;
+  EXPECT_GE(time_elapsed, kRoundedArbitraryDuration);
+}
+
+TEST(Sleep, SleepUntil) {
+  // Ensure we are in a thread context, meaning we are permitted to sleep.
+  ASSERT_NE(get_id(), thread::Id());
+
+  const SystemClock::time_point deadline =
+      SystemClock::now() + kRoundedArbitraryDuration;
+  sleep_until(deadline);
+  EXPECT_GE(SystemClock::now(), deadline);
+}
+
+TEST(Sleep, SleepForInC) {
+  // Ensure we are in a thread context, meaning we are permitted to sleep.
+  ASSERT_NE(get_id(), thread::Id());
+
+  pw_chrono_SystemClock_TimePoint before = pw_chrono_SystemClock_Now();
+  pw_this_thread_SleepFor(kRoundedArbitraryDurationInC);
+  pw_chrono_SystemClock_TickCount time_elapsed =
+      pw_chrono_SystemClock_Now().ticks_since_epoch - before.ticks_since_epoch;
+  EXPECT_GE(time_elapsed, kRoundedArbitraryDurationInC);
+}
+
+TEST(Sleep, SleepUntilInC) {
+  // Ensure we are in a thread context, meaning we are permitted to sleep.
+  ASSERT_NE(get_id(), thread::Id());
+
+  pw_chrono_SystemClock_TimePoint deadline;
+  deadline.ticks_since_epoch = pw_chrono_SystemClock_Now().ticks_since_epoch +
+                               kRoundedArbitraryDurationInC;
+  pw_this_thread_CallSleepUntil(deadline);
+  EXPECT_GE(pw_chrono_SystemClock_Now().ticks_since_epoch,
+            deadline.ticks_since_epoch);
+}
+
+}  // namespace
+}  // namespace pw::this_thread
diff --git a/pw_thread/sleep_facade_test_c.c b/pw_thread/sleep_facade_test_c.c
new file mode 100644
index 0000000..c2f536e
--- /dev/null
+++ b/pw_thread/sleep_facade_test_c.c
@@ -0,0 +1,27 @@
+// 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_thread module sleep API from C. The return values are
+// checked in the main C++ tests.
+
+#include "pw_thread/sleep.h"
+
+void pw_this_thread_CallSleepFor(pw_chrono_SystemClock_TickCount for_at_least) {
+  pw_this_thread_SleepFor(for_at_least);
+}
+
+void pw_this_thread_CallSleepUntil(
+    pw_chrono_SystemClock_TimePoint until_at_least) {
+  pw_this_thread_SleepUntil(until_at_least);
+}
diff --git a/pw_thread/yield.cc b/pw_thread/yield.cc
new file mode 100644
index 0000000..1883c4b
--- /dev/null
+++ b/pw_thread/yield.cc
@@ -0,0 +1,17 @@
+// 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_thread/yield.h"
+
+extern "C" void pw_this_thread_Yield() { pw::this_thread::yield(); }
diff --git a/pw_thread/yield_facade_test.cc b/pw_thread/yield_facade_test.cc
new file mode 100644
index 0000000..2b565f5
--- /dev/null
+++ b/pw_thread/yield_facade_test.cc
@@ -0,0 +1,52 @@
+// 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 "gtest/gtest.h"
+#include "pw_thread/id.h"
+#include "pw_thread/yield.h"
+
+namespace pw::this_thread {
+namespace {
+
+extern "C" {
+
+// Function defined in yield_facade_test_c.c which call the API from C.
+void pw_this_thread_CallYield();
+
+}  // extern "C"
+
+TEST(Yield, CompilesAndRuns) {
+  // Ensure we are in a thread context, meaning we are permitted to sleep.
+  ASSERT_NE(get_id(), thread::Id());
+
+  // Unfortunately we have not thought of a useful way to test yield without
+  // knowing the backend implementation as things like round robin scheduling
+  // may be enabled meaning it may appear like yield is working when it isn't.
+  // For now we just ensure it compiles and we can execute it without a crash.
+  yield();
+}
+
+TEST(Yield, CompilesAndRunsInC) {
+  // Ensure we are in a thread context, meaning we are permitted to sleep.
+  ASSERT_NE(get_id(), thread::Id());
+
+  // Unfortunately we have not thought of a useful way to test yield without
+  // knowing the backend implementation as things like round robin scheduling
+  // may be enabled meaning it may appear like yield is working when it isn't.
+  // For now we just ensure it compiles and we can execute it without a crash.
+  pw_this_thread_CallYield();
+}
+
+}  // namespace
+}  // namespace pw::this_thread
diff --git a/pw_thread/yield_facade_test_c.c b/pw_thread/yield_facade_test_c.c
new file mode 100644
index 0000000..4ff5088
--- /dev/null
+++ b/pw_thread/yield_facade_test_c.c
@@ -0,0 +1,20 @@
+// 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_thread module yield API from C. The return values
+// are checked in the main C++ tests.
+
+#include "pw_thread/yield.h"
+
+void pw_this_thread_CallYield(void) { pw_this_thread_Yield(); }
diff --git a/pw_thread_stl/BUILD b/pw_thread_stl/BUILD
new file mode 100644
index 0000000..6936cb3
--- /dev/null
+++ b/pw_thread_stl/BUILD
@@ -0,0 +1,88 @@
+# 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.
+
+load(
+    "//pw_build:pigweed.bzl",
+    "pw_cc_library",
+)
+
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"])  # Apache License 2.0
+
+pw_cc_library(
+    name = "id_headers",
+    hdrs = [
+        "public/pw_thread_stl/id_inline.h",
+        "public/pw_thread_stl/id_native.h",
+        "public_overrides/pw_thread_backend/id_inline.h",
+        "public_overrides/pw_thread_backend/id_native.h",
+    ],
+    includes = [
+        "public",
+        "public_overrides",
+    ],
+)
+
+pw_cc_library(
+    name = "id",
+    deps = [
+        ":id_headers",
+        "//pw_thread:id_facade",
+    ],
+)
+
+pw_cc_library(
+    name = "sleep_headers",
+    hdrs = [
+        "public/pw_thread_stl/sleep_inline.h",
+        "public_overrides/pw_thread_backend/sleep_inline.h",
+    ],
+    includes = [
+        "public",
+        "public_overrides",
+    ],
+    deps = [
+        "//pw_chrono:system_clock",
+    ],
+)
+
+pw_cc_library(
+    name = "sleep",
+    deps = [
+        ":sleep_headers",
+        "//pw_chrono:system_clock",
+        "//pw_thread:sleep_facade",
+    ],
+)
+
+pw_cc_library(
+    name = "yield_headers",
+    hdrs = [
+        "public/pw_thread_stl/yield_inline.h",
+        "public_overrides/pw_thread_backend/yield_inline.h",
+    ],
+    includes = [
+        "public",
+        "public_overrides",
+    ],
+)
+
+pw_cc_library(
+    name = "yield",
+    deps = [
+        ":yield_headers",
+        "//pw_thread:yield_facade",
+    ],
+)
diff --git a/pw_thread_stl/BUILD.gn b/pw_thread_stl/BUILD.gn
new file mode 100644
index 0000000..f13723e
--- /dev/null
+++ b/pw_thread_stl/BUILD.gn
@@ -0,0 +1,84 @@
+# 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.
+
+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") {
+  include_dirs = [ "public" ]
+  visibility = [ ":*" ]
+}
+
+config("backend_config") {
+  include_dirs = [ "public_overrides" ]
+  visibility = [ ":*" ]
+}
+
+# This target provides the backend for pw::thread::Id & pw::this_thread::get_id.
+pw_source_set("id") {
+  public_configs = [
+    ":public_include_path",
+    ":backend_config",
+  ]
+  public = [
+    "public/pw_thread_stl/id_inline.h",
+    "public/pw_thread_stl/id_native.h",
+    "public_overrides/pw_thread_backend/id_inline.h",
+    "public_overrides/pw_thread_backend/id_native.h",
+  ]
+  deps = [ "$dir_pw_thread:id.facade" ]
+}
+
+# This target provides the backend for pw::this_thread::sleep_{for,until}.
+pw_source_set("sleep") {
+  public_configs = [
+    ":public_include_path",
+    ":backend_config",
+  ]
+  public = [
+    "public/pw_thread_stl/sleep_inline.h",
+    "public_overrides/pw_thread_backend/sleep_inline.h",
+  ]
+  deps = [
+    "$dir_pw_chrono:system_clock",
+    "$dir_pw_thread:sleep.facade",
+  ]
+  assert(
+      pw_chrono_SYSTEM_CLOCK_BACKEND == "" ||
+          pw_chrono_SYSTEM_CLOCK_BACKEND == "$dir_pw_chrono_stl:system_clock",
+      "The STL pw::this_thread::sleep_{for,until} backend only works with " +
+          "the STL pw::chrono::SystemClock backend " +
+          "(pw_chrono_SYSTEM_CLOCK_BACKEND = " +
+          "\"$dir_pw_chrono_stl:system_clock\")")
+}
+
+# This target provides the backend for pw::this_thread::yield.
+pw_source_set("yield") {
+  public_configs = [
+    ":public_include_path",
+    ":backend_config",
+  ]
+  public = [
+    "public/pw_thread_stl/yield_inline.h",
+    "public_overrides/pw_thread_backend/yield_inline.h",
+  ]
+  deps = [ "$dir_pw_thread:yield.facade" ]
+}
+
+pw_doc_group("docs") {
+  sources = [ "docs.rst" ]
+}
diff --git a/pw_thread_stl/docs.rst b/pw_thread_stl/docs.rst
new file mode 100644
index 0000000..1169020
--- /dev/null
+++ b/pw_thread_stl/docs.rst
@@ -0,0 +1,8 @@
+.. _module-pw_thread_stl:
+
+-------------
+pw_thread_stl
+-------------
+This is a set of backends for pw_thread based on the C++ STL. It is not ready
+for use, and is under construction.
+
diff --git a/pw_thread_stl/public/pw_thread_stl/id_inline.h b/pw_thread_stl/public/pw_thread_stl/id_inline.h
new file mode 100644
index 0000000..2d36a73
--- /dev/null
+++ b/pw_thread_stl/public/pw_thread_stl/id_inline.h
@@ -0,0 +1,24 @@
+// 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 <thread>
+
+#include "pw_thread/id.h"
+
+namespace pw::this_thread {
+
+inline thread::Id get_id() noexcept { return std::this_thread::get_id(); }
+
+}  // namespace pw::this_thread
diff --git a/pw_thread_stl/public/pw_thread_stl/id_native.h b/pw_thread_stl/public/pw_thread_stl/id_native.h
new file mode 100644
index 0000000..26a213c
--- /dev/null
+++ b/pw_thread_stl/public/pw_thread_stl/id_native.h
@@ -0,0 +1,22 @@
+// 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 <thread>
+
+namespace pw::thread::backend {
+
+using NativeId = std::thread::id;
+
+}  // namespace pw::thread::backend
diff --git a/pw_thread_stl/public/pw_thread_stl/sleep_inline.h b/pw_thread_stl/public/pw_thread_stl/sleep_inline.h
new file mode 100644
index 0000000..ae9ecaa
--- /dev/null
+++ b/pw_thread_stl/public/pw_thread_stl/sleep_inline.h
@@ -0,0 +1,31 @@
+// 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 <thread>
+
+#include "pw_chrono/system_clock.h"
+#include "pw_thread/sleep.h"
+
+namespace pw::this_thread {
+
+inline void sleep_for(chrono::SystemClock::duration for_at_least) {
+  return std::this_thread::sleep_for(for_at_least);
+}
+
+inline void sleep_until(chrono::SystemClock::time_point until_at_least) {
+  return std::this_thread::sleep_until(until_at_least);
+}
+
+}  // namespace pw::this_thread
diff --git a/pw_thread_stl/public/pw_thread_stl/yield_inline.h b/pw_thread_stl/public/pw_thread_stl/yield_inline.h
new file mode 100644
index 0000000..ec01853
--- /dev/null
+++ b/pw_thread_stl/public/pw_thread_stl/yield_inline.h
@@ -0,0 +1,22 @@
+// 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 <thread>
+
+namespace pw::this_thread {
+
+inline void yield() noexcept { std::this_thread::yield(); }
+
+}  // namespace pw::this_thread
diff --git a/pw_thread_stl/public_overrides/pw_thread_backend/id_inline.h b/pw_thread_stl/public_overrides/pw_thread_backend/id_inline.h
new file mode 100644
index 0000000..56f5e5d
--- /dev/null
+++ b/pw_thread_stl/public_overrides/pw_thread_backend/id_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_thread_stl/id_inline.h"
diff --git a/pw_thread_stl/public_overrides/pw_thread_backend/id_native.h b/pw_thread_stl/public_overrides/pw_thread_backend/id_native.h
new file mode 100644
index 0000000..82aa096
--- /dev/null
+++ b/pw_thread_stl/public_overrides/pw_thread_backend/id_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_thread_stl/id_native.h"
diff --git a/pw_thread_stl/public_overrides/pw_thread_backend/sleep_inline.h b/pw_thread_stl/public_overrides/pw_thread_backend/sleep_inline.h
new file mode 100644
index 0000000..8d0132a
--- /dev/null
+++ b/pw_thread_stl/public_overrides/pw_thread_backend/sleep_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_thread_stl/sleep_inline.h"
diff --git a/pw_thread_stl/public_overrides/pw_thread_backend/yield_inline.h b/pw_thread_stl/public_overrides/pw_thread_backend/yield_inline.h
new file mode 100644
index 0000000..61528f6
--- /dev/null
+++ b/pw_thread_stl/public_overrides/pw_thread_backend/yield_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_thread_stl/yield_inline.h"
diff --git a/targets/host/target_toolchains.gni b/targets/host/target_toolchains.gni
index 1c08013..cc78e7a 100644
--- a/targets/host/target_toolchains.gni
+++ b/targets/host/target_toolchains.gni
@@ -20,6 +20,7 @@
 import("$dir_pw_sync/backend.gni")
 import("$dir_pw_sys_io/backend.gni")
 import("$dir_pw_third_party/nanopb/nanopb.gni")
+import("$dir_pw_thread/backend.gni")
 import("$dir_pw_toolchain/host_clang/toolchains.gni")
 import("$dir_pw_toolchain/host_gcc/toolchains.gni")
 
@@ -79,10 +80,16 @@
 # TODO(amontanez): figure out why std::mutex doesn't work on Windows.
 # These current target configurations do not work on windows.
 _win_incompatible_config = {
+  # Configure backends for pw_sync's facades.
   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"
+
+  # Configure backends for pw_thread's facades.
+  pw_thread_ID_BACKEND = "$dir_pw_thread_stl:id"
+  pw_thread_SLEEP_BACKEND = "$dir_pw_thread_stl:sleep"
+  pw_thread_YIELD_BACKEND = "$dir_pw_thread_stl:yield"
 }
 
 _os_specific_config = {