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