blob: 673a3ca574f89770e612b89adc7404328286d55f [file] [log] [blame]
Ewout van Bekkumf4da4892021-03-05 15:05:37 -08001// 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
Wyatt Heplerf298de42021-03-19 15:06:36 -070016#include "pw_assert/check.h"
Ewout van Bekkumf4da4892021-03-05 15:05:37 -080017#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
24using pw::thread::threadx::Context;
25
26namespace pw::thread {
27namespace {
28#if PW_THREAD_JOINING_ENABLED
29constexpr ULONG kThreadDoneBit = 1;
30#endif // PW_THREAD_JOINING_ENABLED
31} // namespace
32
Ewout van Bekkumcd765c02021-05-14 10:28:26 -070033void Context::ThreadEntryPoint(ULONG void_context_ptr) {
Ewout van Bekkumf4da4892021-03-05 15:05:37 -080034 Context& context = *reinterpret_cast<Context*>(void_context_ptr);
Ewout van Bekkumcd765c02021-05-14 10:28:26 -070035
36 // Invoke the user's thread function. This may never return.
37 context.user_thread_entry_function_(context.user_thread_entry_arg_);
Ewout van Bekkumf4da4892021-03-05 15:05:37 -080038
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
Ewout van Bekkumcd765c02021-05-14 10:28:26 -070055 // 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.
Ewout van Bekkumf4da4892021-03-05 15:05:37 -080058 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
91void 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
113Thread::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.
Ewout van Bekkum5dac14a2021-05-07 14:11:22 -0700127 native_type_->set_in_use(true);
Ewout van Bekkumf4da4892021-03-05 15:05:37 -0800128 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()),
Ewout van Bekkumcd765c02021-05-14 10:28:26 -0700150 Context::ThreadEntryPoint,
Ewout van Bekkumf4da4892021-03-05 15:05:37 -0800151 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
161void 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) {
Ewout van Bekkumcd765c02021-05-14 10:28:26 -0700170 // The task finished (hit end of Context::ThreadEntryPoint) before we
171 // invoked detach, clean up the thread.
Ewout van Bekkumf4da4892021-03-05 15:05:37 -0800172 Context::DeleteThread(*native_type_);
173 } else {
174 // We're detaching before the task finished, defer cleanup to the task at
Ewout van Bekkumcd765c02021-05-14 10:28:26 -0700175 // the end of Context::ThreadEntryPoint.
Ewout van Bekkumf4da4892021-03-05 15:05:37 -0800176 }
177
178 // Update to no longer represent a thread of execution.
179 native_type_ = nullptr;
180}
181
182#if PW_THREAD_JOINING_ENABLED
183void 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