blob: c2b5ef36e96944b583def14b3086b98c8ed47013 [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
Ewout van Bekkum19e753a2021-04-28 18:06:55 -0700115DetachedThread
116==============
117To make it slightly easier and cleaner to spawn detached threads without having
118to worry about thread handles, a wrapper ``DetachedThread()`` function is
119provided which creates a ``Thread`` and immediately detaches it. For example
120instead of:
121
122.. code-block:: cpp
123
124 Thread(options, foo).detach();
125
126You can instead use this helper wrapper to:
127
128.. code-block:: cpp
129
130 DetachedThread(options, foo);
131
132The arguments are directly forwarded to the Thread constructor and ergo exactly
133match the Thread constuctor arguments for creating a thread of execution.
134
135
Ewout van Bekkuma082d7f2021-04-15 14:36:37 -0700136ThreadRoutine & ThreadCore
137==========================
138Threads must either be invoked through a
139``pw::thread::Thread::ThreadRoutine``` style function or implement the
140``pw::thread::ThreadCore`` interface.
141
142.. code-block:: cpp
143
144 namespace pw::thread {
145
146 // This function may return.
147 using Thread::ThreadRoutine = void (*)(void* arg);
148
149 class ThreadCore {
150 public:
151 virtual ~ThreadCore() = default;
152
153 // The public API to start a ThreadCore, note that this may return.
154 void Start() { Run(); }
155
156 private:
157 // This function may return.
158 virtual void Run() = 0;
159 };
160
161 } // namespace pw::thread;
162
163
164To use the ``pw::thread::Thread::ThreadRoutine``, your function must have the
165following signature:
166
167.. code-block:: cpp
168
169 void example_thread_entry_function(void *arg);
170
171
172To invoke a member method of a class a static lambda closure can be used
173to ensure the dispatching closure is not destructed before the thread is
174done executing. For example:
175
176.. code-block:: cpp
177
178 class Foo {
179 public:
180 void DoBar() {}
181 };
182 Foo foo;
183
184 static auto invoke_foo_do_bar = [](void *void_foo_ptr) {
185 // If needed, additional arguments could be set here.
186 static_cast<Foo*>(void_foo_ptr)->DoBar();
187 };
188
189 // Now use the lambda closure as the thread entry, passing the foo's
190 // this as the argument.
191 Thread thread(options, invoke_foo_do_bar, &foo);
192 thread.detach();
193
194
195Alternatively, the aforementioned ``pw::thread::ThreadCore`` interface can be
196be implemented by an object by overriding the private
197``void ThreadCore::Run();`` method. This makes it easier to create a thread, as
198a static lambda closure or function is not needed to dispatch to a member
199function without arguments. For example:
200
201.. code-block:: cpp
202
203 class Foo : public ThreadCore {
204 private:
205 void Run() override {}
206 };
207 Foo foo;
208
209 // Now create the thread, using foo directly.
210 Thread(options, foo).detach();
211
212.. Warning::
213 Because the thread may start after the pw::Thread creation, an object which
214 implements the ThreadCore MUST meet or exceed the lifetime of its thread of
215 execution!