pw_thread: add helper ThreadCore interface

Also extends the pw_thread documentation regarding thread creation.

Change-Id: Ib0a0572060188244b9a5cf91a6bba575057f405b
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/41205
Reviewed-by: Keir Mierle <keir@google.com>
Reviewed-by: Wyatt Hepler <hepler@google.com>
Reviewed-by: Paul Mathieu <paulmathieu@google.com>
Pigweed-Auto-Submit: Ewout van Bekkum <ewout@google.com>
Commit-Queue: Auto-Submit <auto-submit@pigweed.google.com.iam.gserviceaccount.com>
diff --git a/pw_thread/BUILD b/pw_thread/BUILD
index fb892c0..71957c0 100644
--- a/pw_thread/BUILD
+++ b/pw_thread/BUILD
@@ -101,8 +101,12 @@
     name = "thread",
     deps = [
         ":thread_facade",
+        ":thread_core",
         PW_THREAD_THREAD_BACKEND + "_headers",
     ],
+    srcs = [
+        "thread.cc"
+    ],
 )
 
 pw_cc_library(
@@ -113,6 +117,14 @@
 )
 
 pw_cc_library(
+    name = "thread_core",
+    hdrs = [
+        "public/pw_thread/thread_core.h",
+    ],
+    includes = ["public"],
+)
+
+pw_cc_library(
     name = "yield_facade",
     hdrs = [
         "public/pw_thread/yield.h",
diff --git a/pw_thread/BUILD.gn b/pw_thread/BUILD.gn
index a876828..ccd04f3 100644
--- a/pw_thread/BUILD.gn
+++ b/pw_thread/BUILD.gn
@@ -46,7 +46,16 @@
   backend = pw_thread_THREAD_BACKEND
   public_configs = [ ":public_include_path" ]
   public = [ "public/pw_thread/thread.h" ]
-  public_deps = [ ":id" ]
+  public_deps = [
+    ":id",
+    ":thread_core",
+  ]
+  sources = [ "thread.cc" ]
+}
+
+pw_source_set("thread_core") {
+  public_configs = [ ":public_include_path" ]
+  public = [ "public/pw_thread/thread_core.h" ]
 }
 
 pw_facade("yield") {
diff --git a/pw_thread/docs.rst b/pw_thread/docs.rst
index af0f8f6..03b1a5a 100644
--- a/pw_thread/docs.rst
+++ b/pw_thread/docs.rst
@@ -1,8 +1,194 @@
 .. _module-pw_thread:
 
----------
+=========
 pw_thread
----------
-This is a threading module for Pigweed. It is not ready for use, and is under
-construction.
+=========
+The ``pw_thread`` module contains utilities for thread creation and thread
+execution.
 
+.. contents::
+   :local:
+   :depth: 2
+
+.. Warning::
+  This module is still under construction, the API is not yet stable.
+
+---------------
+Thread Creation
+---------------
+The class ``pw::thread::Thread`` can represent a single thread of execution.
+Threads allow multiple functions to execute concurrently.
+
+The Thread's API is C++11 STL
+`std::thread <https://en.cppreference.com/w/cpp/thread/thread>`_ like, meaning
+the object is effectively a thread handle and not an object which contains the
+thread's context. Unlike ``std::thread``, the API requires
+``pw::thread::Options`` as an argument and is limited to only work with
+``pw::thread::ThreadCore`` objects and functions which match the
+``pw::thread::Thread::ThreadRoutine`` signature.
+
+Threads may 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 :ref:`module-pw_sync`)
+
+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.
+
+.. list-table::
+
+  * - *Supported on*
+    - *Backend module*
+  * - FreeRTOS
+    - :ref:`module-pw_thread_freertos`
+  * - ThreadX
+    - :ref:`module-pw_thread_threadx`
+  * - embOS
+    - Planned
+  * - STL
+    - :ref:`module-pw_thread_stl`
+  * - Zephyr
+    - Planned
+  * - CMSIS-RTOS API v2 & RTX5
+    - Planned
+
+
+Options
+=======
+The ``pw::thread::Options`` contains the parameters or attributes needed for a
+thread to start.
+
+Pigweed does not generalize options, instead we strive to give you full control
+where we provide helpers to do this.
+
+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.
+
+Please see the thread creation backend documentation for how their Options work.
+
+.. Note::
+  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. In addition on some schedulers, default options will not work
+  for other reasons.
+
+Detaching & Joining
+===================
+The ``Thread::detach()`` API is always available, to let you separate the
+thread of execution from the thread object, allowing execution to continue
+independently.
+
+The joining API, more specifically ``Thread::join()``, is conditionally
+available depending on the selected backend for thread creation and how it is
+configured. The backend is responsible for providing the
+``PW_THREAD_JOINING_ENABLED`` macro through
+``pw_thread_backend/thread_native.h``. This ensures that any users which include
+``pw_thread/thread.h`` can use this macro if needed.
+
+Please see the selected thread creation backend documentation for how to
+enable joining if it's not already enabled by default.
+
+.. Warning::
+  A constructed ``pw::thread::Thread`` which represents a thread of execution
+  must be EITHER detached or joined, else the destructor will assert!
+
+ThreadRoutine & ThreadCore
+==========================
+Threads must either be invoked through a
+``pw::thread::Thread::ThreadRoutine``` style function or implement the
+``pw::thread::ThreadCore`` interface.
+
+.. code-block:: cpp
+
+  namespace pw::thread {
+
+  // This function may return.
+  using Thread::ThreadRoutine = void (*)(void* arg);
+
+  class ThreadCore {
+   public:
+    virtual ~ThreadCore() = default;
+
+    // The public API to start a ThreadCore, note that this may return.
+    void Start() { Run(); }
+
+   private:
+    // This function may return.
+    virtual void Run() = 0;
+  };
+
+  }  // namespace pw::thread;
+
+
+To use the ``pw::thread::Thread::ThreadRoutine``, your function must have the
+following signature:
+
+.. code-block:: cpp
+
+  void example_thread_entry_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:
+
+.. code-block:: cpp
+
+  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(options, invoke_foo_do_bar, &foo);
+  thread.detach();
+
+
+Alternatively, the aforementioned ``pw::thread::ThreadCore`` interface can be
+be implemented by an object by overriding the private
+``void ThreadCore::Run();`` method. This makes it easier to create a thread, as
+a static lambda closure or function is not needed to dispatch to a member
+function without arguments. For example:
+
+.. code-block:: cpp
+
+  class Foo : public ThreadCore {
+   private:
+    void Run() override {}
+  };
+  Foo foo;
+
+  // Now create the thread, using foo directly.
+  Thread(options, foo).detach();
+
+.. Warning::
+  Because the thread may start after the pw::Thread creation, an object which
+  implements the ThreadCore MUST meet or exceed the lifetime of its thread of
+  execution!
diff --git a/pw_thread/public/pw_thread/thread.h b/pw_thread/public/pw_thread/thread.h
index d4632d5..f609fed 100644
--- a/pw_thread/public/pw_thread/thread.h
+++ b/pw_thread/public/pw_thread/thread.h
@@ -1,4 +1,4 @@
-// Copyright 2020 The Pigweed Authors
+// Copyright 2021 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
@@ -14,6 +14,7 @@
 #pragma once
 
 #include "pw_thread/id.h"
+#include "pw_thread/thread_core.h"
 
 // clang-format off
 // The backend's thread_native header must provide PW_THREAD_JOINING_ENABLED.
@@ -43,10 +44,10 @@
   constexpr Options() = default;
 };
 
-// The class Thread represents a single thread of execution. Threads allow
+// The class Thread can represent a single thread of execution. Threads allow
 // multiple functions to execute concurrently.
 //
-// Threads begin execution immediately upon construction of the associated
+// Threads may 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
@@ -71,44 +72,50 @@
   // Creates a new thread object which represents a thread of execution.
   //
   // Thread functions are permitted to return and must have the following
-  // signature:
+  // ThreadRoutine 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;
+  //   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();
-  //     };
+  //   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();
+  //   // Now use the lambda closure as the thread entry, passing the foo's
+  //   // this as the argument.
+  //   Thread thread(options, 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.
+  // Alternatively a helper ThreadCore interface can be implemented by an object
+  // so that a static lambda closure or function is not needed to dispatch to
+  // a member function without arguments. For example:
+  //   class Foo : public ThreadCore {
+  //    private:
+  //     void Run() override {}
+  //   };
+  //   Foo foo;
   //
-  // WARNING: Options have a default constructor, however default options are
-  // not portable! Default options can only work if threads are dynamically
+  //   // Now create the thread, using foo directly.
+  //   Thread(options, foo).detach();
+  //
+  // Postcondition: The thread get EITHER detached or joined.
+  //
+  // NOTE: 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.
+  // require static thread allocations. In addition on some schedulers
+  // default options may not work for other reasons.
   using ThreadRoutine = void (*)(void* arg);
   Thread(const Options& options, ThreadRoutine entry, void* arg = nullptr);
+  Thread(const Options& options, ThreadCore& thread_core);
 
   // Postcondition: The other thread no longer represents a thread of execution.
   Thread& operator=(Thread&& other);
diff --git a/pw_thread/public/pw_thread/thread_core.h b/pw_thread/public/pw_thread/thread_core.h
new file mode 100644
index 0000000..23e4c62
--- /dev/null
+++ b/pw_thread/public/pw_thread/thread_core.h
@@ -0,0 +1,48 @@
+// Copyright 2021 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
+
+namespace pw::thread {
+
+// An optional virtual interface which can be implemented by objects which are
+// a thread as a helper to use pw::thread::Thread.
+//
+// This wrapper means that the user is not required to provide the indirection
+// callback to call run based on the passed context. For example instead of:
+//
+//   static auto invoke_foo_start = [](void *void_foo_ptr) {
+//     static_cast<Foo*>(void_foo_ptr)->Start();
+//   };
+//   Thread thread(options, invoke_foo_start, &foo).detach();
+//
+// You can instead use the helper constructor in Thread:
+//
+//   Thread thread(options, foo).detach();
+//
+// WARNING: Because the thread may start after the pw::Thread creation, an
+// object which implements the ThreadCore MUST meet or exceed the lifetime of
+// its thread of execution!
+class ThreadCore {
+ public:
+  virtual ~ThreadCore() = default;
+
+  // The public API to start a ThreadCore, note that this may return.
+  void Start() { Run(); }
+
+ private:
+  // This function may return.
+  virtual void Run() = 0;
+};
+
+}  // namespace pw::thread
diff --git a/pw_thread/thread.cc b/pw_thread/thread.cc
new file mode 100644
index 0000000..d2844e5
--- /dev/null
+++ b/pw_thread/thread.cc
@@ -0,0 +1,30 @@
+// Copyright 2021 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/thread.h"
+
+namespace pw::thread {
+namespace {
+
+void StartThreadCore(void* void_thread_core_ptr) {
+  static_cast<ThreadCore*>(void_thread_core_ptr)->Start();
+}
+
+}  // namespace
+
+// Delegating constructor which defers to the facade's constructor.
+Thread::Thread(const Options& options, ThreadCore& thread_core)
+    : Thread(options, StartThreadCore, &thread_core) {}
+
+}  // namespace pw::thread
diff --git a/pw_thread/thread_facade_test.cc b/pw_thread/thread_facade_test.cc
index 0e9cf68..008e2ca 100644
--- a/pw_thread/thread_facade_test.cc
+++ b/pw_thread/thread_facade_test.cc
@@ -1,4 +1,4 @@
-// Copyright 2020 The Pigweed Authors
+// Copyright 2021 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
@@ -141,5 +141,27 @@
   WaitUntilDetachedThreadsCleanedUp();
 }
 
+class SemaphoreReleaser : public ThreadCore {
+ public:
+  pw::sync::BinarySemaphore& semaphore() { return semaphore_; }
+
+ private:
+  void Run() override { semaphore_.release(); }
+
+  sync::BinarySemaphore semaphore_;
+};
+
+TEST(Thread, ThreadCore) {
+  SemaphoreReleaser semaphore_releaser;
+  Thread thread(TestOptionsThread0(), semaphore_releaser);
+  EXPECT_NE(thread.get_id(), Id());
+  EXPECT_TRUE(thread.joinable());
+  thread.detach();
+  EXPECT_EQ(thread.get_id(), Id());
+  EXPECT_FALSE(thread.joinable());
+  semaphore_releaser.semaphore().acquire();
+
+  WaitUntilDetachedThreadsCleanedUp();
+}
 }  // namespace
 }  // namespace pw::thread