C/C++ API Reference
Loading...
Searching...
No Matches
priority.h
1// Copyright 2024 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 <array>
17#include <cstddef>
18#include <cstdint>
19#include <limits>
20
21#include "lib/stdcompat/type_traits.h"
22#include "pw_assert/assert.h"
23#include "pw_numeric/integer_division.h"
24#include "pw_polyfill/language_feature_macros.h"
25
26namespace pw::thread::internal {
27
29
30// Number of named priorities (lowest, low, very low, ..., very high, highest).
31inline constexpr size_t kNamedPriorities = 9;
32
33// Produces a table that distributes priorities between 0 and the highest value.
34// These values are used as offsets when mapping from the native priority type.
35template <typename T>
36PW_CONSTEVAL std::array<T, kNamedPriorities> PriorityOffsets(T highest) {
37 std::array<T, kNamedPriorities> offsets{};
38
39 for (unsigned i = 0; i < kNamedPriorities; ++i) {
40 // Divide the offsets into 8 tiers. The highest value is its own tier.
41 uint64_t priority_value = IntegerDivisionRoundNearest<uint64_t>(
42 static_cast<uint64_t>(highest) * i, kNamedPriorities - 1);
43 // The calculated value never exceeds the max.
44 offsets[i] = static_cast<T>(priority_value);
45 }
46 return offsets;
47}
48
49// Abstract priority level. Specialized for cases when lower numbers represent
50// higher priorities, so that addition and comparisons work in terms of the
51// abtract priority rather than the numeric value.
52template <typename T, T kLowest, T kHighest, bool = kLowest <= kHighest>
53class AbstractLevel {
54 public:
55 using U = std::make_unsigned_t<T>;
56
57 static constexpr U Range() { return kHighest - kLowest; }
58
59 constexpr AbstractLevel() : n_{} {}
60 explicit constexpr AbstractLevel(T value) : n_{value} {}
61 constexpr T value() const { return n_; }
62
63 constexpr bool operator==(AbstractLevel rhs) const { return n_ == rhs.n_; }
64 constexpr bool operator!=(AbstractLevel rhs) const { return n_ != rhs.n_; }
65 constexpr bool operator<(AbstractLevel rhs) const { return n_ < rhs.n_; }
66 constexpr bool operator<=(AbstractLevel rhs) const { return n_ <= rhs.n_; }
67 constexpr bool operator>(AbstractLevel rhs) const { return n_ > rhs.n_; }
68 constexpr bool operator>=(AbstractLevel rhs) const { return n_ >= rhs.n_; }
69
70 constexpr AbstractLevel operator+(U amount) const {
71 return AbstractLevel(static_cast<T>(static_cast<U>(n_) + amount));
72 }
73 constexpr AbstractLevel operator-(U amount) const {
74 return AbstractLevel(static_cast<T>(static_cast<U>(n_) - amount));
75 }
76
77 private:
78 static_assert(kLowest <= kHighest);
79 T n_;
80};
81
82template <typename T, T kLowest, T kHighest>
83struct AbstractLevel<T, kLowest, kHighest, false> {
84 public:
85 using U = std::make_unsigned_t<T>;
86
87 static constexpr U Range() { return kLowest - kHighest; }
88
89 constexpr AbstractLevel() : n_{} {}
90 explicit constexpr AbstractLevel(T value) : n_{value} {}
91 constexpr T value() const { return n_; }
92
93 constexpr bool operator==(AbstractLevel rhs) const { return n_ == rhs.n_; }
94 constexpr bool operator!=(AbstractLevel rhs) const { return n_ != rhs.n_; }
95 constexpr bool operator<(AbstractLevel rhs) const { return n_ > rhs.n_; }
96 constexpr bool operator<=(AbstractLevel rhs) const { return n_ >= rhs.n_; }
97 constexpr bool operator>(AbstractLevel rhs) const { return n_ < rhs.n_; }
98 constexpr bool operator>=(AbstractLevel rhs) const { return n_ <= rhs.n_; }
99
100 constexpr AbstractLevel operator+(U amount) const {
101 return AbstractLevel(static_cast<T>(static_cast<U>(n_) - amount));
102 }
103 constexpr AbstractLevel operator-(U amount) const {
104 return AbstractLevel(static_cast<T>(static_cast<U>(n_) + amount));
105 }
106
107 private:
108 static_assert(kLowest > kHighest);
109 T n_;
110};
111
112// Produces a table that maps named priority levels to their corresponding
113// native priority values.
114template <typename T, bool = std::is_enum_v<T>>
115struct UnderlyingInteger : public std::underlying_type<T> {};
116
117template <typename T>
118struct UnderlyingInteger<T, false> : public cpp20::type_identity<T> {};
119
122template <typename T, T kLowestPriority, T kHighestPriority, T kDefaultPriority>
123class Priority {
124 public:
126 static constexpr bool IsSupported() { return Level::Range() != 0u; }
127
129 constexpr Priority() : Priority(kDefault) {}
130
131 constexpr Priority(const Priority&) = default;
132 constexpr Priority& operator=(const Priority&) = default;
133
137 static constexpr Priority Lowest() { return Priority(kLevels[kLowest]); }
138
140 static constexpr Priority VeryLow() { return Priority(kLevels[kVeryLow]); }
141
143 static constexpr Priority Low() { return Priority(kLevels[kLow]); }
144
146 static constexpr Priority MediumLow() { return Priority(kLevels[kMedLow]); }
147
150 static constexpr Priority Medium() { return Priority(kLevels[kMed]); }
151
153 static constexpr Priority MediumHigh() { return Priority(kLevels[kMedHigh]); }
154
156 static constexpr Priority High() { return Priority(kLevels[kHigh]); }
157
159 static constexpr Priority VeryHigh() { return Priority(kLevels[kVeryHigh]); }
160
164 static constexpr Priority Highest() { return Priority(kLevels[kHighest]); }
165
167 static constexpr Priority Default() { return Priority(); }
168
174 constexpr Priority NextHigher(Priority maximum = Highest()) const {
175 PW_ASSERT(*this != maximum); // Priority cannot exceed the maximum value.
176 return Priority(level_ + 1);
177 }
178
184 constexpr Priority NextLower(Priority minimum = Lowest()) const {
185 PW_ASSERT(*this != minimum); // Priority cannot subceed the minimum value.
186 return Priority(level_ - 1);
187 }
188
190 constexpr Priority NextLowerClamped(Priority minimum = Lowest()) const {
191 return *this > minimum ? Priority(level_ - 1) : *this;
192 }
193
195 constexpr Priority NextHigherClamped(Priority maximum = Highest()) const {
196 return *this < maximum ? Priority(level_ + 1) : *this;
197 }
198
199 constexpr bool operator==(Priority rhs) const { return level_ == rhs.level_; }
200 constexpr bool operator!=(Priority rhs) const { return level_ != rhs.level_; }
201 constexpr bool operator<(Priority rhs) const { return level_ < rhs.level_; }
202 constexpr bool operator<=(Priority rhs) const { return level_ <= rhs.level_; }
203 constexpr bool operator>(Priority rhs) const { return level_ > rhs.level_; }
204 constexpr bool operator>=(Priority rhs) const { return level_ >= rhs.level_; }
205
209 using native_type = T;
210
214 constexpr native_type native() const {
215 return static_cast<native_type>(level_.value());
216 }
217
221 static constexpr Priority FromNative(native_type priority) {
222 const Level level(static_cast<NativeInt>(priority));
223 PW_ASSERT(kLevels[kLowest] <= level); // Priority cannot subceed the min
224 PW_ASSERT(level <= kLevels[kHighest]); // Priority cannot exceed the max
225 return Priority(level);
226 }
227
228 private:
229 static_assert(std::is_integral_v<native_type> || std::is_enum_v<native_type>,
230 "The native priority type must be an integer or enum");
231 static_assert(sizeof(native_type) <= sizeof(uint64_t),
232 "The native priority type cannot be larger than 64 bits");
233
234 using NativeInt = typename UnderlyingInteger<native_type>::type;
235 using Level = AbstractLevel<NativeInt,
236 static_cast<NativeInt>(kLowestPriority),
237 static_cast<NativeInt>(kHighestPriority)>;
238
239 enum : size_t {
240 kLowest,
241 kVeryLow,
242 kLow,
243 kMedLow,
244 kMed,
245 kMedHigh,
246 kHigh,
247 kVeryHigh,
248 kHighest,
249 };
250
251 static_assert(kNamedPriorities == static_cast<size_t>(kHighest) + 1);
252
253 // Produces a table that maps named priority levels to their corresponding
254 // native priority values.
255 static constexpr std::array<Level, kNamedPriorities> kLevels = [] {
256 const std::array offsets = PriorityOffsets(Level::Range());
257
258 std::array<Level, kNamedPriorities> levels{};
259 for (size_t i = 0; i < offsets.size(); ++i) {
260 levels[i] = Level(static_cast<NativeInt>(kLowestPriority)) + offsets[i];
261 }
262 return levels;
263 }();
264
265 static constexpr Level kDefault{static_cast<NativeInt>(kDefaultPriority)};
266 static_assert(kLevels[kLowest] <= kDefault && kDefault <= kLevels[kHighest],
267 "pw::thread::backend::kDefaultPriority must be between "
268 "kLowestPriority and kHighestPriority");
269
270 explicit constexpr Priority(Level level) : level_{level} {}
271
272 Level level_;
273};
274
275} // namespace pw::thread::internal
#define PW_CONSTEVAL
Definition: language_feature_macros.h:40
static constexpr Priority Highest()
Definition: priority.h:164
constexpr Priority()
Constructs a priority at the backend-specified default level.
Definition: priority.h:129
static constexpr Priority Default()
Returns a priority at the backend-specified default level.
Definition: priority.h:167
constexpr Priority NextHigher(Priority maximum=Highest()) const
Definition: priority.h:174
static constexpr bool IsSupported()
True if the pw_thread backend supports more than one priority level.
Definition: priority.h:126
static constexpr Priority VeryLow()
Priority higher than Lowest, but lower than Low, if possible.
Definition: priority.h:140
constexpr Priority NextLowerClamped(Priority minimum=Lowest()) const
Returns the next lower priority, down to the provided maximum.
Definition: priority.h:190
static constexpr Priority High()
Priority higher than MediumHigh, but lower than VeryHigh, if possible.
Definition: priority.h:156
T native_type
Definition: priority.h:209
constexpr Priority NextLower(Priority minimum=Lowest()) const
Definition: priority.h:184
static constexpr Priority MediumHigh()
Priority higher than Medium, but lower than High, if possible.
Definition: priority.h:153
static constexpr Priority Lowest()
Definition: priority.h:137
constexpr Priority NextHigherClamped(Priority maximum=Highest()) const
Returns the next higher priority, up to the provided minimum.
Definition: priority.h:195
static constexpr Priority FromNative(native_type priority)
Definition: priority.h:221
static constexpr Priority Medium()
Definition: priority.h:150
static constexpr Priority VeryHigh()
Priority higher than High, but lower than Highest, if possible.
Definition: priority.h:159
constexpr native_type native() const
Definition: priority.h:214
static constexpr Priority MediumLow()
Priority higher than Low, but lower than Medium, if possible.
Definition: priority.h:146
static constexpr Priority Low()
Priority higher than VeryLow, but lower than MediumLow, if possible.
Definition: priority.h:143