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