xref: /aosp_15_r20/external/pigweed/pw_thread_threadx/thread.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1*61c4878aSAndroid Build Coastguard Worker // Copyright 2021 The Pigweed Authors
2*61c4878aSAndroid Build Coastguard Worker //
3*61c4878aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4*61c4878aSAndroid Build Coastguard Worker // use this file except in compliance with the License. You may obtain a copy of
5*61c4878aSAndroid Build Coastguard Worker // the License at
6*61c4878aSAndroid Build Coastguard Worker //
7*61c4878aSAndroid Build Coastguard Worker //     https://www.apache.org/licenses/LICENSE-2.0
8*61c4878aSAndroid Build Coastguard Worker //
9*61c4878aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*61c4878aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11*61c4878aSAndroid Build Coastguard Worker // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12*61c4878aSAndroid Build Coastguard Worker // License for the specific language governing permissions and limitations under
13*61c4878aSAndroid Build Coastguard Worker // the License.
14*61c4878aSAndroid Build Coastguard Worker #include "pw_thread/thread.h"
15*61c4878aSAndroid Build Coastguard Worker 
16*61c4878aSAndroid Build Coastguard Worker #include "pw_assert/check.h"
17*61c4878aSAndroid Build Coastguard Worker #include "pw_preprocessor/compiler.h"
18*61c4878aSAndroid Build Coastguard Worker #include "pw_thread_threadx/config.h"
19*61c4878aSAndroid Build Coastguard Worker #include "pw_thread_threadx/context.h"
20*61c4878aSAndroid Build Coastguard Worker #include "pw_thread_threadx/options.h"
21*61c4878aSAndroid Build Coastguard Worker #include "tx_event_flags.h"
22*61c4878aSAndroid Build Coastguard Worker 
23*61c4878aSAndroid Build Coastguard Worker using pw::thread::threadx::Context;
24*61c4878aSAndroid Build Coastguard Worker 
25*61c4878aSAndroid Build Coastguard Worker namespace pw::thread {
26*61c4878aSAndroid Build Coastguard Worker namespace {
27*61c4878aSAndroid Build Coastguard Worker #if PW_THREAD_JOINING_ENABLED
28*61c4878aSAndroid Build Coastguard Worker constexpr ULONG kThreadDoneBit = 1;
29*61c4878aSAndroid Build Coastguard Worker #endif  // PW_THREAD_JOINING_ENABLED
30*61c4878aSAndroid Build Coastguard Worker }  // namespace
31*61c4878aSAndroid Build Coastguard Worker 
ThreadEntryPoint(ULONG void_context_ptr)32*61c4878aSAndroid Build Coastguard Worker void Context::ThreadEntryPoint(ULONG void_context_ptr) {
33*61c4878aSAndroid Build Coastguard Worker   Context& context = *reinterpret_cast<Context*>(void_context_ptr);
34*61c4878aSAndroid Build Coastguard Worker 
35*61c4878aSAndroid Build Coastguard Worker   // Invoke the user's thread function. This may never return.
36*61c4878aSAndroid Build Coastguard Worker   context.fn_();
37*61c4878aSAndroid Build Coastguard Worker   context.fn_ = nullptr;
38*61c4878aSAndroid Build Coastguard Worker 
39*61c4878aSAndroid Build Coastguard Worker   // Raise our preemption threshold as a thread only critical section to guard
40*61c4878aSAndroid Build Coastguard Worker   // against join() and detach().
41*61c4878aSAndroid Build Coastguard Worker   UINT original_preemption_threshold = TX_MAX_PRIORITIES;  // Invalid.
42*61c4878aSAndroid Build Coastguard Worker   UINT preemption_success = tx_thread_preemption_change(
43*61c4878aSAndroid Build Coastguard Worker       &context.tcb(), 0, &original_preemption_threshold);
44*61c4878aSAndroid Build Coastguard Worker   PW_DCHECK_UINT_EQ(TX_SUCCESS,
45*61c4878aSAndroid Build Coastguard Worker                     preemption_success,
46*61c4878aSAndroid Build Coastguard Worker                     "Failed to enter thread critical section");
47*61c4878aSAndroid Build Coastguard Worker   if (context.detached()) {
48*61c4878aSAndroid Build Coastguard Worker     // There is no threadsafe way to re-use detached threads, as there's no way
49*61c4878aSAndroid Build Coastguard Worker     // to invoke tx_thread_delete() from the running thread! Joining MUST be
50*61c4878aSAndroid Build Coastguard Worker     // used for this. However to enable unit test coverage we go ahead and clear
51*61c4878aSAndroid Build Coastguard Worker     // this.
52*61c4878aSAndroid Build Coastguard Worker     context.set_in_use(false);
53*61c4878aSAndroid Build Coastguard Worker 
54*61c4878aSAndroid Build Coastguard Worker #if PW_THREAD_JOINING_ENABLED
55*61c4878aSAndroid Build Coastguard Worker     // If the thread handle was detached before the thread finished execution,
56*61c4878aSAndroid Build Coastguard Worker     // i.e. got here, then we are responsible for cleaning up the join event
57*61c4878aSAndroid Build Coastguard Worker     // group.
58*61c4878aSAndroid Build Coastguard Worker     const UINT event_group_result =
59*61c4878aSAndroid Build Coastguard Worker         tx_event_flags_delete(&context.join_event_group());
60*61c4878aSAndroid Build Coastguard Worker     PW_DCHECK_UINT_EQ(TX_SUCCESS,
61*61c4878aSAndroid Build Coastguard Worker                       event_group_result,
62*61c4878aSAndroid Build Coastguard Worker                       "Failed to delete the join event group");
63*61c4878aSAndroid Build Coastguard Worker #endif  // PW_THREAD_JOINING_ENABLED
64*61c4878aSAndroid Build Coastguard Worker 
65*61c4878aSAndroid Build Coastguard Worker     // Note that we do not have to restore our preemption threshold as this
66*61c4878aSAndroid Build Coastguard Worker     // thread is completing execution.
67*61c4878aSAndroid Build Coastguard Worker 
68*61c4878aSAndroid Build Coastguard Worker     // WARNING: The thread at this point continues to be registered with the
69*61c4878aSAndroid Build Coastguard Worker     // kernel in TX_COMPLETED state, as tx_thread_delete cannot be invoked!
70*61c4878aSAndroid Build Coastguard Worker     return;
71*61c4878aSAndroid Build Coastguard Worker   }
72*61c4878aSAndroid Build Coastguard Worker 
73*61c4878aSAndroid Build Coastguard Worker   // Otherwise the task finished before the thread was detached or joined, defer
74*61c4878aSAndroid Build Coastguard Worker   // cleanup to Thread's join() or detach().
75*61c4878aSAndroid Build Coastguard Worker   context.set_thread_done();
76*61c4878aSAndroid Build Coastguard Worker   UINT unused = 0;
77*61c4878aSAndroid Build Coastguard Worker   preemption_success = tx_thread_preemption_change(
78*61c4878aSAndroid Build Coastguard Worker       &context.tcb(), original_preemption_threshold, &unused);
79*61c4878aSAndroid Build Coastguard Worker   PW_DCHECK_UINT_EQ(TX_SUCCESS,
80*61c4878aSAndroid Build Coastguard Worker                     preemption_success,
81*61c4878aSAndroid Build Coastguard Worker                     "Failed to leave thread critical section");
82*61c4878aSAndroid Build Coastguard Worker 
83*61c4878aSAndroid Build Coastguard Worker #if PW_THREAD_JOINING_ENABLED
84*61c4878aSAndroid Build Coastguard Worker   const UINT result =
85*61c4878aSAndroid Build Coastguard Worker       tx_event_flags_set(&context.join_event_group(), kThreadDoneBit, TX_OR);
86*61c4878aSAndroid Build Coastguard Worker   PW_DCHECK_UINT_EQ(TX_SUCCESS, result, "Failed to set the join event");
87*61c4878aSAndroid Build Coastguard Worker #endif  // PW_THREAD_JOINING_ENABLED
88*61c4878aSAndroid Build Coastguard Worker   return;
89*61c4878aSAndroid Build Coastguard Worker }
90*61c4878aSAndroid Build Coastguard Worker 
DeleteThread(Context & context)91*61c4878aSAndroid Build Coastguard Worker void Context::DeleteThread(Context& context) {
92*61c4878aSAndroid Build Coastguard Worker   // Stop the other task first.
93*61c4878aSAndroid Build Coastguard Worker   UINT thread_result = tx_thread_terminate(&context.tcb());
94*61c4878aSAndroid Build Coastguard Worker   PW_CHECK_UINT_EQ(TX_SUCCESS, thread_result, "Failed to terminate the thread");
95*61c4878aSAndroid Build Coastguard Worker 
96*61c4878aSAndroid Build Coastguard Worker   // Delete the thread, removing it out of the kernel.
97*61c4878aSAndroid Build Coastguard Worker   thread_result = tx_thread_delete(&context.tcb());
98*61c4878aSAndroid Build Coastguard Worker   PW_CHECK_UINT_EQ(TX_SUCCESS, thread_result, "Failed to delete the thread");
99*61c4878aSAndroid Build Coastguard Worker 
100*61c4878aSAndroid Build Coastguard Worker   // Mark the context as unused for potential later re-use.
101*61c4878aSAndroid Build Coastguard Worker   context.set_in_use(false);
102*61c4878aSAndroid Build Coastguard Worker 
103*61c4878aSAndroid Build Coastguard Worker #if PW_THREAD_JOINING_ENABLED
104*61c4878aSAndroid Build Coastguard Worker   // Just in case someone abused our API, ensure their use of the event group is
105*61c4878aSAndroid Build Coastguard Worker   // properly handled by the kernel regardless.
106*61c4878aSAndroid Build Coastguard Worker   const UINT event_group_result =
107*61c4878aSAndroid Build Coastguard Worker       tx_event_flags_delete(&context.join_event_group());
108*61c4878aSAndroid Build Coastguard Worker   PW_DCHECK_UINT_EQ(
109*61c4878aSAndroid Build Coastguard Worker       TX_SUCCESS, event_group_result, "Failed to delete the join event group");
110*61c4878aSAndroid Build Coastguard Worker #endif  // PW_THREAD_JOINING_ENABLED
111*61c4878aSAndroid Build Coastguard Worker }
112*61c4878aSAndroid Build Coastguard Worker 
CreateThread(const threadx::Options & options,Function<void ()> && thread_fn)113*61c4878aSAndroid Build Coastguard Worker void Context::CreateThread(const threadx::Options& options,
114*61c4878aSAndroid Build Coastguard Worker                            Function<void()>&& thread_fn) {
115*61c4878aSAndroid Build Coastguard Worker   // Can't use a context more than once.
116*61c4878aSAndroid Build Coastguard Worker   PW_DCHECK(!in_use());
117*61c4878aSAndroid Build Coastguard Worker 
118*61c4878aSAndroid Build Coastguard Worker   // Reset the state of the static context in case it was re-used.
119*61c4878aSAndroid Build Coastguard Worker   set_in_use(true);
120*61c4878aSAndroid Build Coastguard Worker   set_detached(false);
121*61c4878aSAndroid Build Coastguard Worker   set_thread_done(false);
122*61c4878aSAndroid Build Coastguard Worker #if PW_THREAD_JOINING_ENABLED
123*61c4878aSAndroid Build Coastguard Worker   static const char* join_event_group_name = "pw::Thread";
124*61c4878aSAndroid Build Coastguard Worker   const UINT event_group_result = tx_event_flags_create(
125*61c4878aSAndroid Build Coastguard Worker       &join_event_group(), const_cast<char*>(join_event_group_name));
126*61c4878aSAndroid Build Coastguard Worker   PW_DCHECK_UINT_EQ(
127*61c4878aSAndroid Build Coastguard Worker       TX_SUCCESS, event_group_result, "Failed to create the join event group");
128*61c4878aSAndroid Build Coastguard Worker #endif  // PW_THREAD_JOINING_ENABLED
129*61c4878aSAndroid Build Coastguard Worker 
130*61c4878aSAndroid Build Coastguard Worker   // Copy over the thread name.
131*61c4878aSAndroid Build Coastguard Worker   set_name(options.name());
132*61c4878aSAndroid Build Coastguard Worker 
133*61c4878aSAndroid Build Coastguard Worker   // In order to support functions which return and joining, a delegate is
134*61c4878aSAndroid Build Coastguard Worker   // deep copied into the context with a small wrapping function to actually
135*61c4878aSAndroid Build Coastguard Worker   // invoke the task with its arg.
136*61c4878aSAndroid Build Coastguard Worker   set_thread_routine(std::move(thread_fn));
137*61c4878aSAndroid Build Coastguard Worker 
138*61c4878aSAndroid Build Coastguard Worker   const UINT thread_result = tx_thread_create(&tcb(),
139*61c4878aSAndroid Build Coastguard Worker                                               const_cast<char*>(name()),
140*61c4878aSAndroid Build Coastguard Worker                                               Context::ThreadEntryPoint,
141*61c4878aSAndroid Build Coastguard Worker                                               reinterpret_cast<ULONG>(this),
142*61c4878aSAndroid Build Coastguard Worker                                               stack().data(),
143*61c4878aSAndroid Build Coastguard Worker                                               stack().size_bytes(),
144*61c4878aSAndroid Build Coastguard Worker                                               options.priority(),
145*61c4878aSAndroid Build Coastguard Worker                                               options.preemption_threshold(),
146*61c4878aSAndroid Build Coastguard Worker                                               options.time_slice_interval(),
147*61c4878aSAndroid Build Coastguard Worker                                               TX_AUTO_START);
148*61c4878aSAndroid Build Coastguard Worker   PW_CHECK_UINT_EQ(TX_SUCCESS, thread_result, "Failed to create the thread");
149*61c4878aSAndroid Build Coastguard Worker }
150*61c4878aSAndroid Build Coastguard Worker 
Thread(const thread::Options & facade_options,Function<void ()> && entry)151*61c4878aSAndroid Build Coastguard Worker Thread::Thread(const thread::Options& facade_options, Function<void()>&& entry)
152*61c4878aSAndroid Build Coastguard Worker     : native_type_(nullptr) {
153*61c4878aSAndroid Build Coastguard Worker   // Cast the generic facade options to the backend specific option of which
154*61c4878aSAndroid Build Coastguard Worker   // only one type can exist at compile time.
155*61c4878aSAndroid Build Coastguard Worker   auto options = static_cast<const threadx::Options&>(facade_options);
156*61c4878aSAndroid Build Coastguard Worker   PW_DCHECK_NOTNULL(options.context(), "The Context is not optional");
157*61c4878aSAndroid Build Coastguard Worker   native_type_ = options.context();
158*61c4878aSAndroid Build Coastguard Worker   native_type_->CreateThread(options, std::move(entry));
159*61c4878aSAndroid Build Coastguard Worker }
160*61c4878aSAndroid Build Coastguard Worker 
detach()161*61c4878aSAndroid Build Coastguard Worker void Thread::detach() {
162*61c4878aSAndroid Build Coastguard Worker   PW_CHECK(joinable());
163*61c4878aSAndroid Build Coastguard Worker 
164*61c4878aSAndroid Build Coastguard Worker   tx_thread_suspend(&native_type_->tcb());
165*61c4878aSAndroid Build Coastguard Worker   native_type_->set_detached();
166*61c4878aSAndroid Build Coastguard Worker   const bool thread_done = native_type_->thread_done();
167*61c4878aSAndroid Build Coastguard Worker   tx_thread_resume(&native_type_->tcb());
168*61c4878aSAndroid Build Coastguard Worker 
169*61c4878aSAndroid Build Coastguard Worker   if (thread_done) {
170*61c4878aSAndroid Build Coastguard Worker     // The task finished (hit end of Context::ThreadEntryPoint) before we
171*61c4878aSAndroid Build Coastguard Worker     // invoked detach, clean up the thread.
172*61c4878aSAndroid Build Coastguard Worker     Context::DeleteThread(*native_type_);
173*61c4878aSAndroid Build Coastguard Worker   } else {
174*61c4878aSAndroid Build Coastguard Worker     // We're detaching before the task finished, defer cleanup to the task at
175*61c4878aSAndroid Build Coastguard Worker     // the end of Context::ThreadEntryPoint.
176*61c4878aSAndroid Build Coastguard Worker   }
177*61c4878aSAndroid Build Coastguard Worker 
178*61c4878aSAndroid Build Coastguard Worker   // Update to no longer represent a thread of execution.
179*61c4878aSAndroid Build Coastguard Worker   native_type_ = nullptr;
180*61c4878aSAndroid Build Coastguard Worker }
181*61c4878aSAndroid Build Coastguard Worker 
182*61c4878aSAndroid Build Coastguard Worker #if PW_THREAD_JOINING_ENABLED
join()183*61c4878aSAndroid Build Coastguard Worker void Thread::join() {
184*61c4878aSAndroid Build Coastguard Worker   PW_CHECK(joinable());
185*61c4878aSAndroid Build Coastguard Worker   PW_CHECK(this_thread::get_id() != get_id());
186*61c4878aSAndroid Build Coastguard Worker 
187*61c4878aSAndroid Build Coastguard Worker   ULONG actual_flags = 0;
188*61c4878aSAndroid Build Coastguard Worker   const UINT result = tx_event_flags_get(&native_type_->join_event_group(),
189*61c4878aSAndroid Build Coastguard Worker                                          kThreadDoneBit,
190*61c4878aSAndroid Build Coastguard Worker                                          TX_OR_CLEAR,
191*61c4878aSAndroid Build Coastguard Worker                                          &actual_flags,
192*61c4878aSAndroid Build Coastguard Worker                                          TX_WAIT_FOREVER);
193*61c4878aSAndroid Build Coastguard Worker   PW_DCHECK_UINT_EQ(TX_SUCCESS, result, "Failed to get the join event");
194*61c4878aSAndroid Build Coastguard Worker 
195*61c4878aSAndroid Build Coastguard Worker   // No need for a critical section here as the thread at this point is
196*61c4878aSAndroid Build Coastguard Worker   // waiting to be deleted.
197*61c4878aSAndroid Build Coastguard Worker   Context::DeleteThread(*native_type_);
198*61c4878aSAndroid Build Coastguard Worker 
199*61c4878aSAndroid Build Coastguard Worker   // Update to no longer represent a thread of execution.
200*61c4878aSAndroid Build Coastguard Worker   native_type_ = nullptr;
201*61c4878aSAndroid Build Coastguard Worker }
202*61c4878aSAndroid Build Coastguard Worker #endif  // PW_THREAD_JOINING_ENABLED
203*61c4878aSAndroid Build Coastguard Worker 
204*61c4878aSAndroid Build Coastguard Worker }  // namespace pw::thread
205