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/assert.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
RunThread(ULONG void_context_ptr)33 void Context::RunThread(ULONG void_context_ptr) {
34 Context& context = *reinterpret_cast<Context*>(void_context_ptr);
35 context.entry_(context.arg_);
36
37 // Raise our preemption threshold as a thread only critical section to guard
38 // against join() and detach().
39 UINT original_preemption_threshold = TX_MAX_PRIORITIES; // Invalid.
40 UINT preemption_success = tx_thread_preemption_change(
41 &context.tcb(), 0, &original_preemption_threshold);
42 PW_DCHECK_UINT_EQ(TX_SUCCESS,
43 preemption_success,
44 "Failed to enter thread critical section");
45 if (context.detached()) {
46 // There is no threadsafe way to re-use detached threads, as there's no way
47 // to invoke tx_thread_delete() from the running thread! Joining MUST be
48 // used for this. However to enable unit test coverage we go ahead and clear
49 // this.
50 context.set_in_use(false);
51
52 #if PW_THREAD_JOINING_ENABLED
53 // Just in case someone abused our API, ensure their use of the event group
54 // is properly handled by the kernel regardless.
55 const UINT event_group_result =
56 tx_event_flags_delete(&context.join_event_group());
57 PW_DCHECK_UINT_EQ(TX_SUCCESS,
58 event_group_result,
59 "Failed to delete the join event group");
60 #endif // PW_THREAD_JOINING_ENABLED
61
62 // Note that we do not have to restore our preemption threshold as this
63 // thread is completing execution.
64
65 // WARNING: The thread at this point continues to be registered with the
66 // kernel in TX_COMPLETED state, as tx_thread_delete cannot be invoked!
67 return;
68 }
69
70 // Otherwise the task finished before the thread was detached or joined, defer
71 // cleanup to Thread's join() or detach().
72 context.set_thread_done();
73 UINT unused = 0;
74 preemption_success = tx_thread_preemption_change(
75 &context.tcb(), original_preemption_threshold, &unused);
76 PW_DCHECK_UINT_EQ(TX_SUCCESS,
77 preemption_success,
78 "Failed to leave thread critical section");
79
80 #if PW_THREAD_JOINING_ENABLED
81 const UINT result =
82 tx_event_flags_set(&context.join_event_group(), kThreadDoneBit, TX_OR);
83 PW_DCHECK_UINT_EQ(TX_SUCCESS, result, "Failed to set the join event");
84 #endif // PW_THREAD_JOINING_ENABLED
85 return;
86 }
87
DeleteThread(Context & context)88 void Context::DeleteThread(Context& context) {
89 // Stop the other task first.
90 UINT thread_result = tx_thread_terminate(&context.tcb());
91 PW_CHECK_UINT_EQ(TX_SUCCESS, thread_result, "Failed to terminate the thread");
92
93 // Delete the thread, removing it out of the kernel.
94 thread_result = tx_thread_delete(&context.tcb());
95 PW_CHECK_UINT_EQ(TX_SUCCESS, thread_result, "Failed to delete the thread");
96
97 // Mark the context as unused for potential later re-use.
98 context.set_in_use(false);
99
100 #if PW_THREAD_JOINING_ENABLED
101 // Just in case someone abused our API, ensure their use of the event group is
102 // properly handled by the kernel regardless.
103 const UINT event_group_result =
104 tx_event_flags_delete(&context.join_event_group());
105 PW_DCHECK_UINT_EQ(
106 TX_SUCCESS, event_group_result, "Failed to delete the join event group");
107 #endif // PW_THREAD_JOINING_ENABLED
108 }
109
Thread(const thread::Options & facade_options,ThreadRoutine entry,void * arg)110 Thread::Thread(const thread::Options& facade_options,
111 ThreadRoutine entry,
112 void* arg)
113 : native_type_(nullptr) {
114 // Cast the generic facade options to the backend specific option of which
115 // only one type can exist at compile time.
116 auto options = static_cast<const threadx::Options&>(facade_options);
117 PW_DCHECK_NOTNULL(options.context(), "The Context is not optional");
118 native_type_ = options.context();
119
120 // Can't use a context more than once.
121 PW_DCHECK(!native_type_->in_use());
122
123 // Reset the state of the static context in case it was re-used.
124 native_type_->set_in_use(false);
125 native_type_->set_detached(false);
126 native_type_->set_thread_done(false);
127 #if PW_THREAD_JOINING_ENABLED
128 static const char* join_event_group_name = "pw::Thread";
129 const UINT event_group_result =
130 tx_event_flags_create(&options.context()->join_event_group(),
131 const_cast<char*>(join_event_group_name));
132 PW_DCHECK_UINT_EQ(
133 TX_SUCCESS, event_group_result, "Failed to create the join event group");
134 #endif // PW_THREAD_JOINING_ENABLED
135
136 // Copy over the thread name.
137 native_type_->set_name(options.name());
138
139 // In order to support functions which return and joining, a delegate is
140 // deep copied into the context with a small wrapping function to actually
141 // invoke the task with its arg.
142 native_type_->set_thread_routine(entry, arg);
143
144 const UINT thread_result =
145 tx_thread_create(&options.context()->tcb(),
146 const_cast<char*>(native_type_->name()),
147 Context::RunThread,
148 reinterpret_cast<ULONG>(native_type_),
149 options.context()->stack().data(),
150 options.context()->stack().size_bytes(),
151 options.priority(),
152 options.preemption_threshold(),
153 options.time_slice_interval(),
154 TX_AUTO_START);
155 PW_CHECK_UINT_EQ(TX_SUCCESS, thread_result, "Failed to create the thread");
156 }
157
detach()158 void Thread::detach() {
159 PW_CHECK(joinable());
160
161 tx_thread_suspend(&native_type_->tcb());
162 native_type_->set_detached();
163 const bool thread_done = native_type_->thread_done();
164 tx_thread_resume(&native_type_->tcb());
165
166 if (thread_done) {
167 // The task finished (hit end of Context::RunThread) before we invoked
168 // detach, clean up the thread.
169 Context::DeleteThread(*native_type_);
170 } else {
171 // We're detaching before the task finished, defer cleanup to the task at
172 // the end of Context::RunThread.
173 }
174
175 // Update to no longer represent a thread of execution.
176 native_type_ = nullptr;
177 }
178
179 #if PW_THREAD_JOINING_ENABLED
join()180 void Thread::join() {
181 PW_CHECK(joinable());
182 PW_CHECK(this_thread::get_id() != get_id());
183
184 ULONG actual_flags = 0;
185 const UINT result = tx_event_flags_get(&native_type_->join_event_group(),
186 kThreadDoneBit,
187 TX_OR_CLEAR,
188 &actual_flags,
189 TX_WAIT_FOREVER);
190 PW_DCHECK_UINT_EQ(TX_SUCCESS, result, "Failed to get the join event");
191
192 // No need for a critical section here as the thread at this point is
193 // waiting to be deleted.
194 Context::DeleteThread(*native_type_);
195
196 // Update to no longer represent a thread of execution.
197 native_type_ = nullptr;
198 }
199 #endif // PW_THREAD_JOINING_ENABLED
200
201 } // namespace pw::thread
202