pw_thread: adds thread creation

Adds a std::thread like API through a new pw_thread facade based
on the STL's std::thread API, however with more restricted function
entry routine format support and backend specific option support
to permit non-portable configuration of thread settings/parameters
including static context allocations.

In addition this provides an initial set of backends based on using
the STL's std::thread directly.

Change-Id: Ib8c3cbdc434044e204e67e58d861a40e4acec9b4
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/30920
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/BUILD.gn b/BUILD.gn
index ae068a3..1399035 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -304,6 +304,7 @@
       "$dir_pw_string:tests",
       "$dir_pw_sync:tests",
       "$dir_pw_thread:tests",
+      "$dir_pw_thread_stl:tests",
       "$dir_pw_tokenizer:tests",
       "$dir_pw_trace:tests",
       "$dir_pw_trace_tokenized:tests",
diff --git a/pw_thread/BUILD b/pw_thread/BUILD
index 4335978..073897c 100644
--- a/pw_thread/BUILD
+++ b/pw_thread/BUILD
@@ -25,6 +25,7 @@
 # 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_THREAD_BACKEND = "//pw_thread_stl:thread"
 PW_THREAD_YIELD_BACKEND = "//pw_thread_stl:yield"
 
 pw_cc_library(
@@ -85,6 +86,33 @@
 )
 
 pw_cc_library(
+    name = "thread_facade",
+    hdrs = [
+        "public/pw_thread/thread.h",
+    ],
+    includes = ["public"],
+    deps = [
+        ":id_facade",
+        PW_THREAD_THREAD_BACKEND + "_headers",
+    ],
+)
+
+pw_cc_library(
+    name = "thread",
+    deps = [
+        ":thread_facade",
+        PW_THREAD_THREAD_BACKEND + "_headers",
+    ],
+)
+
+pw_cc_library(
+    name = "thread_backend",
+    deps = [
+       PW_THREAD_THREAD_BACKEND,
+    ],
+)
+
+pw_cc_library(
     name = "yield_facade",
     hdrs = [
         "public/pw_thread/yield.h",
@@ -114,6 +142,32 @@
     ],
 )
 
+pw_cc_library(
+    name = "test_threads_header",
+    hdrs = [
+        "public/pw_thread/test_threads.h",
+    ],
+    deps = [
+        ":thread",
+    ],
+)
+
+# To instantiate this as a pw_cc_test, depend on this pw_cc_library and the
+# pw_cc_library which implements the backend for test_threads_header. See
+# //pw_thread:thread_backend_test as an example.
+pw_cc_library(
+    name = "thread_facade_test",
+    srcs = [
+        "thread_facade_test.cc",
+    ],
+    deps = [
+        ":thread",
+        ":id",
+        ":test_threads_header",
+        "//pw_unit_test",
+    ],
+)
+
 pw_cc_test(
     name = "id_facade_test",
     srcs = [
diff --git a/pw_thread/BUILD.gn b/pw_thread/BUILD.gn
index 436c1a1..b8a1dc1 100644
--- a/pw_thread/BUILD.gn
+++ b/pw_thread/BUILD.gn
@@ -42,6 +42,13 @@
   sources = [ "sleep.cc" ]
 }
 
+pw_facade("thread") {
+  backend = pw_thread_THREAD_BACKEND
+  public_configs = [ ":public_include_path" ]
+  public = [ "public/pw_thread/thread.h" ]
+  public_deps = [ ":id" ]
+}
+
 pw_facade("yield") {
   backend = pw_thread_YIELD_BACKEND
   public_configs = [ ":public_include_path" ]
@@ -77,6 +84,28 @@
   ]
 }
 
+if (pw_thread_THREAD_BACKEND != "") {
+  pw_source_set("test_threads") {
+    public_configs = [ ":public_include_path" ]
+    public = [ "public/pw_thread/test_threads.h" ]
+    public_deps = [ ":thread" ]
+  }
+
+  # To instantiate this facade test based on a selected backend to provide
+  # test_threads you can create a pw_test target which depends on this
+  # pw_source_set and a pw_source_set which provides the implementation of
+  # test_threads. See "$dir_pw_thread_stl:thread_backend_test" as an example.
+  pw_source_set("thread_facade_test") {
+    sources = [ "thread_facade_test.cc" ]
+    deps = [
+      ":id",
+      ":test_threads",
+      ":thread",
+      dir_pw_unit_test,
+    ]
+  }
+}
+
 pw_test("yield_facade_test") {
   enable_if = pw_thread_YIELD_BACKEND != "" && pw_thread_ID_BACKEND != ""
   sources = [
diff --git a/pw_thread/backend.gni b/pw_thread/backend.gni
index 98a2738..6ecd378 100644
--- a/pw_thread/backend.gni
+++ b/pw_thread/backend.gni
@@ -19,6 +19,9 @@
   # 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::Thread to create threads.
+  pw_thread_THREAD_BACKEND = ""
+
   # Backend for the pw_thread module's pw::thread::yield.
   pw_thread_YIELD_BACKEND = ""
 
diff --git a/pw_thread/public/pw_thread/test_threads.h b/pw_thread/public/pw_thread/test_threads.h
new file mode 100644
index 0000000..3b8d765
--- /dev/null
+++ b/pw_thread/public/pw_thread/test_threads.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 "pw_thread/thread.h"
+
+namespace pw::thread::test {
+
+// Two test threads are used to verify the thread facade.
+const Options& TestOptionsThread0();
+const Options& TestOptionsThread1();
+
+}  // namespace pw::thread::test
diff --git a/pw_thread/public/pw_thread/thread.h b/pw_thread/public/pw_thread/thread.h
new file mode 100644
index 0000000..d4632d5
--- /dev/null
+++ b/pw_thread/public/pw_thread/thread.h
@@ -0,0 +1,183 @@
+// 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/id.h"
+
+// clang-format off
+// The backend's thread_native header must provide PW_THREAD_JOINING_ENABLED.
+#include "pw_thread_backend/thread_native.h"
+// clang-format on
+
+namespace pw::thread {
+
+// The Options contains the parameters needed for a thread to start.
+//
+// Options are backend specific and ergo the generic base class cannot be
+// directly instantiated.
+//
+// The attributes which can be set through the options are backend specific
+// but may contain things like the thread name, priority, scheduling policy,
+// core/processor affinity, and/or an optional reference to a pre-allocated
+// Context (the collection of memory allocations needed for a thread to run).
+//
+// Options shall NOT permit starting as detached, this must be done explicitly
+// through the Thread API.
+//
+// Options must not contain any memory needed for a thread to run (TCB,
+// stack, etc.). The Options may be deleted or re-used immediately after
+// starting a thread.
+class Options {
+ protected:
+  constexpr Options() = default;
+};
+
+// The class Thread represents a single thread of execution. Threads allow
+// multiple functions to execute concurrently.
+//
+// Threads begin execution immediately upon construction of the associated
+// thread object (pending any OS scheduling delays), starting at the top-level
+// function provided as a constructor argument. The return value of the
+// top-level function is ignored. The top-level function may communicate its
+// return value by modifying shared variables (which may require
+// synchronization, see pw_sync and std::atomic)
+//
+// Thread objects may also be in the state that does not represent any thread
+// (after default construction, move from, detach, or join), and a thread of
+// execution may be not associated with any thread objects (after detach).
+//
+// No two Thread objects may represent the same thread of execution; Thread is
+// not CopyConstructible or CopyAssignable, although it is MoveConstructible and
+// MoveAssignable.
+class Thread {
+ public:
+  using native_handle_type = backend::NativeThreadHandle;
+
+  // Creates a new thread object which does not represent a thread of execution
+  // yet.
+  Thread();
+
+  // Creates a new thread object which represents a thread of execution.
+  //
+  // Thread functions are permitted to return and must have the following
+  // signature:
+  //   void example_function(void *arg);
+  //
+  // To invoke a member method of a class a static lambda closure can be used
+  // to ensure the dispatching closure is not destructed before the thread is
+  // done executing. For example:
+  //     class Foo {
+  //      public:
+  //       void DoBar() {}
+  //     };
+  //     Foo foo;
+  //
+  //     static auto invoke_foo_do_bar = [](void *void_foo_ptr) {
+  //         //  If needed, additional arguments could be set here.
+  //         static_cast<Foo*>(void_foo_ptr)->DoBar();
+  //     };
+  //
+  //     // Now use the lambda closure as the thread entry, passing the foo's
+  //     // this as the argument.
+  //     Thread thread(invoke_foo_do_bar, &foo);
+  //     thread.detach();
+  //
+  // Postcondition: The thread get EITHER detached or joined. Note that this can
+  // sometimes be done through backend specific options during construction.
+  //
+  // WARNING: Options have a default constructor, however default options are
+  // not portable! Default options can only work if threads are dynamically
+  // allocated by default, meaning default options cannot work on backends which
+  // require static thread allocations.
+  //
+  // TODO(ewout): use a pw::Closure like object which supports the current API
+  // capability but with type safety and ease of executing member functions.
+  // We may consider a size constrained version of Zircon's fit's movable
+  // closure concept, which does not rely on dynamic allocation unlike
+  // std::function. Note that the plan is NOT to support any arbitrary number of
+  // arguments.
+  using ThreadRoutine = void (*)(void* arg);
+  Thread(const Options& options, ThreadRoutine entry, void* arg = nullptr);
+
+  // Postcondition: The other thread no longer represents a thread of execution.
+  Thread& operator=(Thread&& other);
+
+  // Precondition: The thread must have been EITHER detached or joined.
+  ~Thread();
+
+  Thread(const Thread&) = delete;
+  Thread(Thread&&) = delete;
+  Thread& operator=(const Thread&) = delete;
+
+  // Returns a value of Thread::id identifying the thread associated with *this.
+  // If there is no thread associated, default constructed Thread::id is
+  // returned.
+  Id get_id() const;
+
+  // Checks if the Thread object identifies an active thread of execution which
+  // has not yet been detached. Specifically, returns true if get_id() !=
+  // pw::Thread::id() && detach() has NOT been invoked. So a default
+  // constructed thread is not joinable and neither is one which was detached.
+  //
+  // A thread that has not started or has finished executing code which was
+  // never detached, but has not yet been joined is still considered an active
+  // thread of execution and is therefore joinable.
+  bool joinable() const { return get_id() != Id(); }
+
+#if PW_THREAD_JOINING_ENABLED
+  // Blocks the current thread until the thread identified by *this finishes its
+  // execution.
+  //
+  // The completion of the thread identified by *this synchronizes with the
+  // corresponding successful return from join().
+  //
+  // No synchronization is performed on *this itself. Concurrently calling
+  // join() on the same thread object from multiple threads constitutes a data
+  // race that results in undefined behavior.
+  //
+  // Precondition: The thread must have been NEITHER detached nor joined.
+  //
+  // Postcondition: After calling detach *this no longer owns any thread.
+  void join();
+#endif  // PW_THREAD_JOINING_ENABLED
+
+  // Separates the thread of execution from the thread object, allowing
+  // execution to continue independently. Any allocated resources will be freed
+  // once the thread exits.
+  //
+  // Precondition: The thread must have been NEITHER detached nor joined.
+  //
+  // Postcondition: After calling detach *this no longer owns any thread.
+  void detach();
+
+  // Exchanges the underlying handles of two thread objects.
+  void swap(Thread& other);
+
+  native_handle_type native_handle();
+
+ private:
+  // Note that just like std::thread, this is effectively just a pointer or
+  // reference to the native thread -- this does not contain any memory needed
+  // for the thread to execute.
+  //
+  // This may contain more than the native thread handle to enable functionality
+  // which is not always available such as joining, which may require a
+  // reference to a binary semaphore, or passing arguments to the thread's
+  // function.
+  backend::NativeThread native_type_;
+};
+
+}  // namespace pw::thread
+
+#include "pw_thread_backend/thread_inline.h"
diff --git a/pw_thread/thread_facade_test.cc b/pw_thread/thread_facade_test.cc
new file mode 100644
index 0000000..510fe03
--- /dev/null
+++ b/pw_thread/thread_facade_test.cc
@@ -0,0 +1,144 @@
+// 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 <atomic>
+
+#include "gtest/gtest.h"
+#include "pw_thread/id.h"
+#include "pw_thread/test_threads.h"
+#include "pw_thread/thread.h"
+
+using pw::thread::test::TestOptionsThread0;
+using pw::thread::test::TestOptionsThread1;
+
+namespace pw::thread {
+namespace {
+
+TEST(Thread, DefaultIds) {
+  Thread not_executing_thread;
+  EXPECT_EQ(not_executing_thread.get_id(), Id());
+}
+
+static void SetBoolTrue(void* arg) {
+  *reinterpret_cast<std::atomic<bool>*>(arg) = true;
+}
+
+// TODO(ewout): this currently doesn't work on backends with dynamic context
+// allocation disabled. Perhaps we could require the backend to provide
+// thread options for the facade unit tests to use?
+
+#if PW_THREAD_JOINING_ENABLED
+TEST(Thread, Join) {
+  Thread thread;
+  EXPECT_FALSE(thread.joinable());
+  std::atomic<bool> thread_ran = false;
+  thread = Thread(TestOptionsThread0(), SetBoolTrue, &thread_ran);
+  EXPECT_TRUE(thread.joinable());
+  thread.join();
+  EXPECT_EQ(thread.get_id(), Id());
+  EXPECT_TRUE(thread_ran);
+}
+#endif  // PW_THREAD_JOINING_ENABLED
+
+TEST(Thread, Detach) {
+  Thread thread;
+  std::atomic<bool> thread_ran = false;
+  thread = Thread(TestOptionsThread0(), SetBoolTrue, &thread_ran);
+  EXPECT_NE(thread.get_id(), Id());
+#if PW_THREAD_JOINING_ENABLED
+  EXPECT_TRUE(thread.joinable());
+#endif  // PW_THREAD_JOINING_ENABLED
+  thread.detach();
+  EXPECT_EQ(thread.get_id(), Id());
+#if PW_THREAD_JOINING_ENABLED
+  EXPECT_FALSE(thread.joinable());
+#endif  // PW_THREAD_JOINING_ENABLED
+  // We could use a synchronization primitive here to wait until the thread
+  // finishes running to check that thread_ran is true, but that's covered by
+  // pw_sync instead. Instead we use an idiotic busy loop.
+  // - Assume our clock is < 6Ghz
+  // - Assume we can check the clock in a single cycle
+  // - Wait for up to 1/10th of a second @ 6Ghz, this may be a long period on a
+  //   slower (i.e. real) machine.
+  constexpr uint64_t kMaxIterations = 6'000'000'000 / 10;
+  for (uint64_t i = 0; i < kMaxIterations; ++i) {
+    if (thread_ran)
+      break;
+  }
+  EXPECT_TRUE(thread_ran);
+}
+
+TEST(Thread, SwapWithoutExecution) {
+  Thread thread_0;
+  Thread thread_1;
+
+  // Make sure we can swap threads which are not associated with any execution.
+  thread_0.swap(thread_1);
+}
+
+TEST(Thread, SwapWithOneExecuting) {
+  Thread thread_0;
+  EXPECT_EQ(thread_0.get_id(), Id());
+
+  static std::atomic<bool> thread_ran = false;
+  Thread thread_1(TestOptionsThread1(), SetBoolTrue, &thread_ran);
+  EXPECT_NE(thread_1.get_id(), Id());
+
+  thread_0.swap(thread_1);
+  EXPECT_NE(thread_0.get_id(), Id());
+  EXPECT_EQ(thread_1.get_id(), Id());
+
+  thread_0.detach();
+  EXPECT_EQ(thread_0.get_id(), Id());
+}
+
+TEST(Thread, SwapWithTwoExecuting) {
+  static std::atomic<bool> thread_a_ran = false;
+  Thread thread_0(TestOptionsThread0(), SetBoolTrue, &thread_a_ran);
+  static std::atomic<bool> thread_b_ran = false;
+  Thread thread_1(TestOptionsThread1(), SetBoolTrue, &thread_b_ran);
+  const Id thread_a_id = thread_0.get_id();
+  EXPECT_NE(thread_a_id, Id());
+  const Id thread_b_id = thread_1.get_id();
+  EXPECT_NE(thread_b_id, Id());
+  EXPECT_NE(thread_a_id, thread_b_id);
+
+  thread_0.swap(thread_1);
+  EXPECT_EQ(thread_1.get_id(), thread_a_id);
+  EXPECT_EQ(thread_0.get_id(), thread_b_id);
+
+  thread_0.detach();
+  EXPECT_EQ(thread_0.get_id(), Id());
+  thread_1.detach();
+  EXPECT_EQ(thread_1.get_id(), Id());
+}
+
+TEST(Thread, MoveOperator) {
+  Thread thread_0;
+  EXPECT_EQ(thread_0.get_id(), Id());
+
+  std::atomic<bool> thread_ran = false;
+  Thread thread_1(TestOptionsThread1(), SetBoolTrue, &thread_ran);
+  EXPECT_NE(thread_1.get_id(), Id());
+
+  thread_0 = std::move(thread_1);
+  EXPECT_NE(thread_0.get_id(), Id());
+  EXPECT_EQ(thread_1.get_id(), Id());
+
+  thread_0.detach();
+  EXPECT_EQ(thread_0.get_id(), Id());
+}
+
+}  // namespace
+}  // namespace pw::thread
diff --git a/pw_thread_stl/BUILD b/pw_thread_stl/BUILD
index 6936cb3..e7624a6 100644
--- a/pw_thread_stl/BUILD
+++ b/pw_thread_stl/BUILD
@@ -15,6 +15,7 @@
 load(
     "//pw_build:pigweed.bzl",
     "pw_cc_library",
+    "pw_cc_test",
 )
 
 package(default_visibility = ["//visibility:public"])
@@ -68,6 +69,48 @@
 )
 
 pw_cc_library(
+    name = "thread_headers",
+    hdrs = [
+        "public/pw_thread_stl/options.h",
+        "public/pw_thread_stl/thread_inline.h",
+        "public/pw_thread_stl/thread_native.h",
+        "public_overrides/pw_thread_backend/thread_inline.h",
+        "public_overrides/pw_thread_backend/thread_native.h",
+    ],
+    includes = [
+        "public",
+        "public_overrides",
+    ],
+)
+
+pw_cc_library(
+    name = "thread",
+    deps = [
+        ":thread_headers",
+        "//pw_thread:thread_facade",
+    ],
+)
+
+pw_cc_library(
+    name = "test_threads",
+    deps = [
+        "//pw_thread:thread_facade",
+        "//pw_thread:test_threads_header",
+    ],
+    srcs = [
+        "test_threads.cc",
+    ]
+)
+
+pw_cc_test(
+    name = "thread_backend_test",
+    deps = [
+        "//pw_thread:thread_facade_test",
+        ":test_threads",
+    ]
+)
+
+pw_cc_library(
     name = "yield_headers",
     hdrs = [
         "public/pw_thread_stl/yield_inline.h",
diff --git a/pw_thread_stl/BUILD.gn b/pw_thread_stl/BUILD.gn
index f13723e..37ccf9b 100644
--- a/pw_thread_stl/BUILD.gn
+++ b/pw_thread_stl/BUILD.gn
@@ -17,6 +17,8 @@
 import("$dir_pw_build/target_types.gni")
 import("$dir_pw_chrono/backend.gni")
 import("$dir_pw_docgen/docs.gni")
+import("$dir_pw_thread/backend.gni")
+import("$dir_pw_unit_test/test.gni")
 
 config("public_include_path") {
   include_dirs = [ "public" ]
@@ -43,6 +45,24 @@
   deps = [ "$dir_pw_thread:id.facade" ]
 }
 
+# This target provides the backend for pw::thread::Thread with joining
+# joining capability.
+pw_source_set("thread") {
+  public_configs = [
+    ":public_include_path",
+    ":backend_config",
+  ]
+  public = [
+    "public/pw_thread_stl/options.h",
+    "public/pw_thread_stl/thread_inline.h",
+    "public/pw_thread_stl/thread_native.h",
+    "public_overrides/pw_thread_backend/thread_inline.h",
+    "public_overrides/pw_thread_backend/thread_native.h",
+  ]
+  allow_circular_includes_from = [ "$dir_pw_thread:thread.facade" ]
+  deps = [ "$dir_pw_thread:thread.facade" ]
+}
+
 # This target provides the backend for pw::this_thread::sleep_{for,until}.
 pw_source_set("sleep") {
   public_configs = [
@@ -79,6 +99,24 @@
   deps = [ "$dir_pw_thread:yield.facade" ]
 }
 
+pw_test_group("tests") {
+  tests = [ ":thread_backend_test" ]
+}
+
+pw_source_set("test_threads") {
+  public_deps = [ "$dir_pw_thread:test_threads" ]
+  sources = [ "test_threads.cc" ]
+  deps = [ "$dir_pw_thread:thread" ]
+}
+
+pw_test("thread_backend_test") {
+  enable_if = pw_thread_THREAD_BACKEND == "$dir_pw_thread_stl:thread"
+  deps = [
+    ":test_threads",
+    "$dir_pw_thread:thread_facade_test",
+  ]
+}
+
 pw_doc_group("docs") {
   sources = [ "docs.rst" ]
 }
diff --git a/pw_thread_stl/public/pw_thread_stl/options.h b/pw_thread_stl/public/pw_thread_stl/options.h
new file mode 100644
index 0000000..26f5bf5
--- /dev/null
+++ b/pw_thread_stl/public/pw_thread_stl/options.h
@@ -0,0 +1,26 @@
+// 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/thread.h"
+
+namespace pw::thread::stl {
+
+// Unfortunately std::thread:attributes was not accepted into the C++ standard.
+// Instead, users are expected to start the thread and after dynamically adjust
+// the thread's attributes using std::thread::native_handle based on the native
+// threading APIs.
+class Options : public thread::Options {};
+
+}  // namespace pw::thread::stl
diff --git a/pw_thread_stl/public/pw_thread_stl/thread_inline.h b/pw_thread_stl/public/pw_thread_stl/thread_inline.h
new file mode 100644
index 0000000..018e807
--- /dev/null
+++ b/pw_thread_stl/public/pw_thread_stl/thread_inline.h
@@ -0,0 +1,47 @@
+// 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 {
+
+inline Thread::Thread() : native_type_() {}
+
+inline Thread::Thread(const Options&, ThreadRoutine entry, void* arg) {
+  native_type_ = std::thread(entry, arg);
+}
+
+inline Thread& Thread::operator=(Thread&& other) {
+  native_type_ = std::move(other.native_type_);
+  return *this;
+}
+
+inline Thread::~Thread() = default;
+
+inline Id Thread::get_id() const { return native_type_.get_id(); }
+
+inline void Thread::join() { native_type_.join(); }
+
+inline void Thread::detach() { native_type_.detach(); }
+
+inline void Thread::swap(Thread& other) {
+  native_type_.swap(other.native_handle());
+}
+
+inline Thread::native_handle_type Thread::native_handle() {
+  return native_type_;
+}
+
+}  // namespace pw::thread
diff --git a/pw_thread_stl/public/pw_thread_stl/thread_native.h b/pw_thread_stl/public/pw_thread_stl/thread_native.h
new file mode 100644
index 0000000..d3592de
--- /dev/null
+++ b/pw_thread_stl/public/pw_thread_stl/thread_native.h
@@ -0,0 +1,25 @@
+// 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>
+
+#define PW_THREAD_JOINING_ENABLED 1
+
+namespace pw::thread::backend {
+
+using NativeThread = std::thread;
+using NativeThreadHandle = std::thread&;
+
+}  // namespace pw::thread::backend
diff --git a/pw_thread_stl/public_overrides/pw_thread_backend/thread_inline.h b/pw_thread_stl/public_overrides/pw_thread_backend/thread_inline.h
new file mode 100644
index 0000000..2a52ee8
--- /dev/null
+++ b/pw_thread_stl/public_overrides/pw_thread_backend/thread_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/thread_inline.h"
diff --git a/pw_thread_stl/public_overrides/pw_thread_backend/thread_native.h b/pw_thread_stl/public_overrides/pw_thread_backend/thread_native.h
new file mode 100644
index 0000000..272a595
--- /dev/null
+++ b/pw_thread_stl/public_overrides/pw_thread_backend/thread_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/thread_native.h"
diff --git a/pw_thread_stl/test_threads.cc b/pw_thread_stl/test_threads.cc
new file mode 100644
index 0000000..e9e35bf
--- /dev/null
+++ b/pw_thread_stl/test_threads.cc
@@ -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.
+
+#include "pw_thread_stl/options.h"
+
+namespace pw::thread::test {
+
+// The STL doesn't offer any options so the default constructed options are used
+// directly.
+
+const Options& TestOptionsThread0() {
+  static constexpr stl::Options thread_0_options;
+  return thread_0_options;
+}
+
+const Options& TestOptionsThread1() {
+  static constexpr stl::Options thread_1_options;
+  return thread_1_options;
+}
+
+}  // namespace pw::thread::test
diff --git a/targets/host/target_toolchains.gni b/targets/host/target_toolchains.gni
index fc205f8..aa64fa5 100644
--- a/targets/host/target_toolchains.gni
+++ b/targets/host/target_toolchains.gni
@@ -90,6 +90,7 @@
   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"
+  pw_thread_THREAD_BACKEND = "$dir_pw_thread_stl:thread"
 }
 
 _os_specific_config = {