Pigweed
 
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
28// Number of named priorities (lowest, low, very low, ..., very high, highest).
29inline constexpr size_t kNamedPriorities = 9;
30
31// Produces a table that distributes priorities between 0 and the highest value.
32// These values are used as offsets when mapping from the native priority type.
33PW_CONSTEVAL std::array<uint32_t, kNamedPriorities> PriorityOffsets(
34 uint32_t highest) {
35 std::array<uint32_t, kNamedPriorities> offsets{};
36
37 for (unsigned i = 0; i < kNamedPriorities; ++i) {
38 // Divide the offsets into 8 tiers. The highest value is its own tier.
39 uint64_t priority_value = IntegerDivisionRoundNearest<uint64_t>(
40 static_cast<uint64_t>(highest) * i, kNamedPriorities - 1);
41 // The calculated value never exceeds the max uint32_t.
42 offsets[i] = static_cast<uint32_t>(priority_value);
43 }
44 return offsets;
45}
46
47// Abstract priority level. Specialized for cases when lower numbers represent
48// higher priorities, so that addition and comparisons work in terms of the
49// abtract priority rather than the numeric value.
50template <typename T, T kLowest, T kHighest, bool = kLowest <= kHighest>
51class AbstractLevel {
52 public:
53 static constexpr uint32_t Range() { return kHighest - kLowest; }
54
55 constexpr AbstractLevel() : n_{} {}
56 explicit constexpr AbstractLevel(T value) : n_{value} {}
57 constexpr T value() const { return n_; }
58
59 constexpr bool operator==(AbstractLevel rhs) const { return n_ == rhs.n_; }
60 constexpr bool operator!=(AbstractLevel rhs) const { return n_ != rhs.n_; }
61 constexpr bool operator<(AbstractLevel rhs) const { return n_ < rhs.n_; }
62 constexpr bool operator<=(AbstractLevel rhs) const { return n_ <= rhs.n_; }
63 constexpr bool operator>(AbstractLevel rhs) const { return n_ > rhs.n_; }
64 constexpr bool operator>=(AbstractLevel rhs) const { return n_ >= rhs.n_; }
65
66 constexpr AbstractLevel operator+(uint32_t amount) const {
67 return AbstractLevel(static_cast<T>(static_cast<uint32_t>(n_) + amount));
68 }
69 constexpr AbstractLevel operator-(uint32_t amount) const {
70 return AbstractLevel(static_cast<T>(static_cast<uint32_t>(n_) - amount));
71 }
72
73 private:
74 static_assert(kLowest <= kHighest);
75 T n_;
76};
77
78template <typename T, T kLowest, T kHighest>
79struct AbstractLevel<T, kLowest, kHighest, false> {
80 public:
81 static constexpr uint32_t Range() { return kLowest - kHighest; }
82
83 constexpr AbstractLevel() : n_{} {}
84 explicit constexpr AbstractLevel(T value) : n_{value} {}
85 constexpr T value() const { return n_; }
86
87 constexpr bool operator==(AbstractLevel rhs) const { return n_ == rhs.n_; }
88 constexpr bool operator!=(AbstractLevel rhs) const { return n_ != rhs.n_; }
89 constexpr bool operator<(AbstractLevel rhs) const { return n_ > rhs.n_; }
90 constexpr bool operator<=(AbstractLevel rhs) const { return n_ >= rhs.n_; }
91 constexpr bool operator>(AbstractLevel rhs) const { return n_ < rhs.n_; }
92 constexpr bool operator>=(AbstractLevel rhs) const { return n_ <= rhs.n_; }
93
94 constexpr AbstractLevel operator+(uint32_t amount) const {
95 return AbstractLevel(static_cast<T>(static_cast<uint32_t>(n_) - amount));
96 }
97 constexpr AbstractLevel operator-(uint32_t amount) const {
98 return AbstractLevel(static_cast<T>(static_cast<uint32_t>(n_) + amount));
99 }
100
101 private:
102 static_assert(kLowest > kHighest);
103 T n_;
104};
105
106// Produces a table that maps named priority levels to their corresponding
107// native priority values.
108template <typename T, bool = std::is_enum_v<T>>
109struct UnderlyingInteger : public std::underlying_type<T> {};
110
111template <typename T>
112struct UnderlyingInteger<T, false> : public cpp20::type_identity<T> {};
113
116template <typename T, T kLowestPriority, T kHighestPriority, T kDefaultPriority>
117class Priority {
118 public:
120 static constexpr bool IsSupported() { return Level::Range() != 0u; }
121
123 constexpr Priority() : Priority(kDefault) {}
124
125 constexpr Priority(const Priority&) = default;
126 constexpr Priority& operator=(const Priority&) = default;
127
131 static constexpr Priority Lowest() { return Priority(kLevels[kLowest]); }
132
134 static constexpr Priority VeryLow() { return Priority(kLevels[kVeryLow]); }
135
137 static constexpr Priority Low() { return Priority(kLevels[kLow]); }
138
140 static constexpr Priority MediumLow() { return Priority(kLevels[kMedLow]); }
141
144 static constexpr Priority Medium() { return Priority(kLevels[kMed]); }
145
147 static constexpr Priority MediumHigh() { return Priority(kLevels[kMedHigh]); }
148
150 static constexpr Priority High() { return Priority(kLevels[kHigh]); }
151
153 static constexpr Priority VeryHigh() { return Priority(kLevels[kVeryHigh]); }
154
158 static constexpr Priority Highest() { return Priority(kLevels[kHighest]); }
159
161 static constexpr Priority Default() { return Priority(); }
162
168 constexpr Priority NextHigher(Priority maximum = Highest()) const {
169 PW_ASSERT(*this != maximum); // Priority cannot exceed the maximum value.
170 return Priority(level_ + 1);
171 }
172
178 constexpr Priority NextLower(Priority minimum = Lowest()) const {
179 PW_ASSERT(*this != minimum); // Priority cannot subceed the minimum value.
180 return Priority(level_ - 1);
181 }
182
184 constexpr Priority NextLowerClamped(Priority minimum = Lowest()) const {
185 return *this > minimum ? Priority(level_ - 1) : *this;
186 }
187
189 constexpr Priority NextHigherClamped(Priority maximum = Highest()) const {
190 return *this < maximum ? Priority(level_ + 1) : *this;
191 }
192
193 constexpr bool operator==(Priority rhs) const { return level_ == rhs.level_; }
194 constexpr bool operator!=(Priority rhs) const { return level_ != rhs.level_; }
195 constexpr bool operator<(Priority rhs) const { return level_ < rhs.level_; }
196 constexpr bool operator<=(Priority rhs) const { return level_ <= rhs.level_; }
197 constexpr bool operator>(Priority rhs) const { return level_ > rhs.level_; }
198 constexpr bool operator>=(Priority rhs) const { return level_ >= rhs.level_; }
199
203 using native_type = T;
204
208 constexpr native_type native() const {
209 return static_cast<native_type>(level_.value());
210 }
211
215 static constexpr Priority FromNative(native_type priority) {
216 const Level level(static_cast<NativeInt>(priority));
217 PW_ASSERT(kLevels[kLowest] <= level); // Priority cannot subceed the min
218 PW_ASSERT(level <= kLevels[kHighest]); // Priority cannot exceed the max
219 return Priority(level);
220 }
221
222 private:
223 static_assert(std::is_integral_v<native_type> || std::is_enum_v<native_type>,
224 "The native priority type must be an integer or enum");
225 static_assert(sizeof(native_type) <= sizeof(uint32_t),
226 "The native priority type cannot be larger than 32 bits");
227
228 using NativeInt = typename UnderlyingInteger<native_type>::type;
229 using Level = AbstractLevel<NativeInt,
230 static_cast<NativeInt>(kLowestPriority),
231 static_cast<NativeInt>(kHighestPriority)>;
232
233 enum : size_t {
234 kLowest,
235 kVeryLow,
236 kLow,
237 kMedLow,
238 kMed,
239 kMedHigh,
240 kHigh,
241 kVeryHigh,
242 kHighest,
243 };
244
245 static_assert(kNamedPriorities == static_cast<size_t>(kHighest) + 1);
246
247 // Produces a table that maps named priority levels to their corresponding
248 // native priority values.
249 static constexpr std::array<Level, kNamedPriorities> kLevels = [] {
250 const std::array offsets = PriorityOffsets(Level::Range());
251
252 std::array<Level, kNamedPriorities> levels{};
253 for (size_t i = 0; i < offsets.size(); ++i) {
254 levels[i] = Level(static_cast<NativeInt>(kLowestPriority)) + offsets[i];
255 }
256 return levels;
257 }();
258
259 static constexpr Level kDefault{static_cast<NativeInt>(kDefaultPriority)};
260 static_assert(kLevels[kLowest] <= kDefault && kDefault <= kLevels[kHighest],
261 "pw::thread::backend::kDefaultPriority must be between "
262 "kLowestPriority and kHighestPriority");
263
264 explicit constexpr Priority(Level level) : level_{level} {}
265
266 Level level_;
267};
268
269} // namespace pw::thread::internal