blob: 03b1a5a2780f2605226bca58744472ef14be817f [file] [log] [blame]
Ewout van Bekkume3b56032020-12-22 12:00:18 -08001.. _module-pw_thread:
2
Ewout van Bekkuma082d7f2021-04-15 14:36:37 -07003=========
Ewout van Bekkume3b56032020-12-22 12:00:18 -08004pw_thread
Ewout van Bekkuma082d7f2021-04-15 14:36:37 -07005=========
6The ``pw_thread`` module contains utilities for thread creation and thread
7execution.
Ewout van Bekkume3b56032020-12-22 12:00:18 -08008
Ewout van Bekkuma082d7f2021-04-15 14:36:37 -07009.. contents::
10 :local:
11 :depth: 2
12
13.. Warning::
14 This module is still under construction, the API is not yet stable.
15
16---------------
17Thread Creation
18---------------
19The class ``pw::thread::Thread`` can represent a single thread of execution.
20Threads allow multiple functions to execute concurrently.
21
22The Thread's API is C++11 STL
23`std::thread <https://en.cppreference.com/w/cpp/thread/thread>`_ like, meaning
24the object is effectively a thread handle and not an object which contains the
25thread's context. Unlike ``std::thread``, the API requires
26``pw::thread::Options`` as an argument and is limited to only work with
27``pw::thread::ThreadCore`` objects and functions which match the
28``pw::thread::Thread::ThreadRoutine`` signature.
29
30Threads may begin execution immediately upon construction of the associated
31thread object (pending any OS scheduling delays), starting at the top-level
32function provided as a constructor argument. The return value of the
33top-level function is ignored. The top-level function may communicate its
34return value by modifying shared variables (which may require
35synchronization, see :ref:`module-pw_sync`)
36
37Thread objects may also be in the state that does not represent any thread
38(after default construction, move from, detach, or join), and a thread of
39execution may be not associated with any thread objects (after detach).
40
41No two Thread objects may represent the same thread of execution; Thread is
42not CopyConstructible or CopyAssignable, although it is MoveConstructible and
43MoveAssignable.
44
45.. list-table::
46
47 * - *Supported on*
48 - *Backend module*
49 * - FreeRTOS
50 - :ref:`module-pw_thread_freertos`
51 * - ThreadX
52 - :ref:`module-pw_thread_threadx`
53 * - embOS
54 - Planned
55 * - STL
56 - :ref:`module-pw_thread_stl`
57 * - Zephyr
58 - Planned
59 * - CMSIS-RTOS API v2 & RTX5
60 - Planned
61
62
63Options
64=======
65The ``pw::thread::Options`` contains the parameters or attributes needed for a
66thread to start.
67
68Pigweed does not generalize options, instead we strive to give you full control
69where we provide helpers to do this.
70
71Options are backend specific and ergo the generic base class cannot be
72directly instantiated.
73
74The attributes which can be set through the options are backend specific
75but may contain things like the thread name, priority, scheduling policy,
76core/processor affinity, and/or an optional reference to a pre-allocated
77Context (the collection of memory allocations needed for a thread to run).
78
79Options shall NOT permit starting as detached, this must be done explicitly
80through the Thread API.
81
82Options must not contain any memory needed for a thread to run (TCB,
83stack, etc.). The Options may be deleted or re-used immediately after
84starting a thread.
85
86Please see the thread creation backend documentation for how their Options work.
87
88.. Note::
89 Options have a default constructor, however default options are not portable!
90 Default options can only work if threads are dynamically allocated by default,
91 meaning default options cannot work on backends which require static thread
92 allocations. In addition on some schedulers, default options will not work
93 for other reasons.
94
95Detaching & Joining
96===================
97The ``Thread::detach()`` API is always available, to let you separate the
98thread of execution from the thread object, allowing execution to continue
99independently.
100
101The joining API, more specifically ``Thread::join()``, is conditionally
102available depending on the selected backend for thread creation and how it is
103configured. The backend is responsible for providing the
104``PW_THREAD_JOINING_ENABLED`` macro through
105``pw_thread_backend/thread_native.h``. This ensures that any users which include
106``pw_thread/thread.h`` can use this macro if needed.
107
108Please see the selected thread creation backend documentation for how to
109enable joining if it's not already enabled by default.
110
111.. Warning::
112 A constructed ``pw::thread::Thread`` which represents a thread of execution
113 must be EITHER detached or joined, else the destructor will assert!
114
115ThreadRoutine & ThreadCore
116==========================
117Threads must either be invoked through a
118``pw::thread::Thread::ThreadRoutine``` style function or implement the
119``pw::thread::ThreadCore`` interface.
120
121.. code-block:: cpp
122
123 namespace pw::thread {
124
125 // This function may return.
126 using Thread::ThreadRoutine = void (*)(void* arg);
127
128 class ThreadCore {
129 public:
130 virtual ~ThreadCore() = default;
131
132 // The public API to start a ThreadCore, note that this may return.
133 void Start() { Run(); }
134
135 private:
136 // This function may return.
137 virtual void Run() = 0;
138 };
139
140 } // namespace pw::thread;
141
142
143To use the ``pw::thread::Thread::ThreadRoutine``, your function must have the
144following signature:
145
146.. code-block:: cpp
147
148 void example_thread_entry_function(void *arg);
149
150
151To invoke a member method of a class a static lambda closure can be used
152to ensure the dispatching closure is not destructed before the thread is
153done executing. For example:
154
155.. code-block:: cpp
156
157 class Foo {
158 public:
159 void DoBar() {}
160 };
161 Foo foo;
162
163 static auto invoke_foo_do_bar = [](void *void_foo_ptr) {
164 // If needed, additional arguments could be set here.
165 static_cast<Foo*>(void_foo_ptr)->DoBar();
166 };
167
168 // Now use the lambda closure as the thread entry, passing the foo's
169 // this as the argument.
170 Thread thread(options, invoke_foo_do_bar, &foo);
171 thread.detach();
172
173
174Alternatively, the aforementioned ``pw::thread::ThreadCore`` interface can be
175be implemented by an object by overriding the private
176``void ThreadCore::Run();`` method. This makes it easier to create a thread, as
177a static lambda closure or function is not needed to dispatch to a member
178function without arguments. For example:
179
180.. code-block:: cpp
181
182 class Foo : public ThreadCore {
183 private:
184 void Run() override {}
185 };
186 Foo foo;
187
188 // Now create the thread, using foo directly.
189 Thread(options, foo).detach();
190
191.. Warning::
192 Because the thread may start after the pw::Thread creation, an object which
193 implements the ThreadCore MUST meet or exceed the lifetime of its thread of
194 execution!