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 #pragma once
15 
16 #include "pw_assert/assert.h"
17 #include "pw_thread/thread.h"
18 #include "pw_thread_threadx/config.h"
19 #include "pw_thread_threadx/context.h"
20 #include "tx_api.h"
21 
22 namespace pw::thread::threadx {
23 
24 // pw::thread::Options for ThreadX.
25 //
26 // Example usage:
27 //
28 //   // Uses the default priority and time slice interval (which may be
29 //   // disabled), but specifies a custom name and pre-allocated context.
30 //   // Note that the preemption threshold is disabled by default.
31 //   pw::thread::Thread example_thread(
32 //     pw::thread::threadx::Options()
33 //         .set_name("example_thread"),
34 //         .set_context(static_example_thread_context),
35 //     example_thread_function);
36 //
37 //   // Specifies the name, priority, time slice interval, and pre-allocated
38 //   // context, but does not use a preemption threshold.
39 //   pw::thread::Thread static_example_thread(
40 //     pw::thread::threadx::Options()
41 //         .set_name("static_example_thread")
42 //         .set_priority(kFooPriority)
43 //         .set_time_slice_interval(1)
44 //         .set_context(static_example_thread_context),
45 //     example_thread_function);
46 //
47 class Options : public thread::Options {
48  public:
49   constexpr Options() = default;
50   constexpr Options(const Options&) = default;
51   constexpr Options(Options&& other) = default;
52 
53   // Sets the name for the ThreadX thread, note that this will be deep copied
54   // into the context and may be truncated based on
55   // PW_THREAD_THREADX_CONFIG_MAX_THREAD_NAME_LEN.
set_name(const char * name)56   constexpr Options set_name(const char* name) {
57     name_ = name;
58     return *this;
59   }
60 
61   // Sets the priority for the ThreadX thread from 0 through 31, where a value
62   // of 0 represents the highest priority, see ThreadX tx_thread_create for
63   // more detail.
set_priority(UINT priority)64   constexpr Options set_priority(UINT priority) {
65     PW_DASSERT(priority <= PW_THREAD_THREADX_CONFIG_MIN_PRIORITY);
66     priority_ = priority;
67     return *this;
68   }
69 
70   // Optionally sets the preemption threshold for the ThreadX thread from 0
71   // through 31.
72   //
73   // Only priorities higher than this level (i.e. lower number) are allowed to
74   // preempt this thread. In other words this allows the thread to specify the
75   // priority ceiling for disabling preemption. Threads that have a higher
76   // priority than the ceiling are still allowed to preempt while those with
77   // less than the ceiling are not allowed to preempt.
78   //
79   // Not setting the preemption threshold or explicitly specifying a value
80   // equal to the priority disables preemption threshold.
81   //
82   // Time slicing is disabled while the preemption threshold is enabled, i.e.
83   // not equal to the priority, even if a time slice interval was specified.
84   //
85   // The preemption threshold can be adjusted at run time, this only sets the
86   // initial threshold.
87   //
88   // Precondition: preemption_threshold <= priority
set_preemption_threshold(UINT preemption_threshold)89   constexpr Options set_preemption_threshold(UINT preemption_threshold) {
90     PW_DASSERT(preemption_threshold < PW_THREAD_THREADX_CONFIG_MIN_PRIORITY);
91     possible_preemption_threshold_ = preemption_threshold;
92     return *this;
93   }
94 
95   // Sets the number of ticks this thread is allowed to run before other ready
96   // threads of the same priority are given a chance to run.
97   //
98   // Time slicing is disabled while the preemption threshold is enabled, i.e.
99   // not equal to the priority, even if a time slice interval was specified.
100   //
101   // A value of TX_NO_TIME_SLICE (a value of 0) disables time-slicing of this
102   // thread.
103   //
104   // Using time slicing results in a slight amount of system overhead, threads
105   // with a unique priority should consider TX_NO_TIME_SLICE.
set_time_slice_interval(ULONG time_slice_interval)106   constexpr Options set_time_slice_interval(ULONG time_slice_interval) {
107     time_slice_interval_ = time_slice_interval;
108     return *this;
109   }
110 
111   // Set the pre-allocated context (all memory needed to run a thread), see the
112   // pw::thread::threadx::Context for more detail.
set_context(Context & context)113   constexpr Options set_context(Context& context) {
114     context_ = &context;
115     return *this;
116   }
117 
118  private:
119   friend thread::Thread;
120   // Note that the default name may end up truncated due to
121   // PW_THREAD_THREADX_CONFIG_MAX_THREAD_NAME_LEN.
122   static constexpr char kDefaultName[] = "pw::Thread";
123 
name()124   const char* name() const { return name_; }
priority()125   UINT priority() const { return priority_; }
preemption_threshold()126   UINT preemption_threshold() const {
127     return possible_preemption_threshold_.value_or(priority_);
128   }
time_slice_interval()129   ULONG time_slice_interval() const { return time_slice_interval_; }
context()130   Context* context() const { return context_; }
131 
132   const char* name_ = kDefaultName;
133   UINT priority_ = config::kDefaultPriority;
134   // A default value cannot be used for the preemption threshold as it would
135   // have to be based on the selected priority.
136   std::optional<UINT> possible_preemption_threshold_ = std::nullopt;
137   ULONG time_slice_interval_ = config::kDefaultTimeSliceInterval;
138   Context* context_ = nullptr;
139 };
140 
141 }  // namespace pw::thread::threadx
142