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