C/C++ API Reference
Loading...
Searching...
No Matches
clock_tree.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 <atomic>
17#include <cstdint>
18#include <memory>
19#include <mutex>
20#include <optional>
21
22#include "pw_assert/assert.h"
23#include "pw_clock_tree/internal/deferred_init.h"
24#include "pw_status/status.h"
25#include "pw_status/try.h"
26#include "pw_sync/interrupt_spin_lock.h"
27#include "pw_sync/mutex.h"
28
30namespace pw::clock_tree {
31
33
45class Element {
46 public:
47 constexpr Element(bool may_block) : may_block_(may_block) {}
48 virtual ~Element() = default;
49
51 uint32_t ref_count() const { return ref_count_; }
52
54 bool may_block() const { return may_block_; }
55
56 // Not copyable or movable
57 Element(const Element&) = delete;
58 Element(const Element&&) = delete;
59 Element& operator=(const Element&) = delete;
60 Element& operator=(const Element&&) = delete;
61
68 Status Acquire() { return DoAcquire(); }
69
76 Status Release() { return DoRelease(); }
77
89 Status AcquireWith(Element& element_with) {
90 PW_TRY(element_with.Acquire());
91 Status status = Acquire();
92 // Ignore any error on release because it should not fail if the acquire
93 // succeeded, and there's no reasonable alternative.
94 element_with.Release().IgnoreError();
95 return status;
96 }
97
98 protected:
110 virtual Status DoAcquireLocked() = 0;
111
127 virtual Status DoReleaseLocked() = 0;
128
130 uint32_t IncRef() { return ++ref_count_; }
131
133 uint32_t DecRef() { return --ref_count_; }
134
136 virtual Status DoEnable() = 0;
137
142 virtual Status DoDisable() { return OkStatus(); }
143
144 private:
146 uint32_t ref_count_ = 0;
147
149 const bool may_block_;
150
152 virtual Status DoAcquire() = 0;
153
155 virtual Status DoRelease() = 0;
156};
157
160class ElementBlocking : public Element {
161 public:
162 static constexpr bool kMayBlock = true;
163 static constexpr bool kMayFail = true;
164 constexpr ElementBlocking() : Element(kMayBlock) {}
165
166 protected:
167 sync::Mutex& lock() { return *mutex_; }
168
169 private:
170 // sync::Mutex is not constexpr-constructible so use DeferredInit so we can
171 // keep our constexpr constructor.
172 internal::DeferredInit<sync::Mutex> mutex_;
173
175 std::lock_guard lock(this->lock());
176 return DoAcquireLocked();
177 }
179 std::lock_guard lock(this->lock());
180 return DoReleaseLocked();
181 }
182};
183
187 public:
188 static constexpr bool kMayBlock = false;
189 static constexpr bool kMayFail = true;
190 constexpr ElementNonBlockingMightFail() : Element(kMayBlock) {}
191
192 protected:
193 sync::InterruptSpinLock& lock() { return spin_lock_; }
194
195 private:
196 sync::InterruptSpinLock spin_lock_;
197
199 std::lock_guard lock(this->lock());
200 return DoAcquireLocked();
201 }
203 std::lock_guard lock(this->lock());
204 return DoReleaseLocked();
205 }
206};
207
211 public:
212 static constexpr bool kMayFail = false;
213
215 void Acquire() { PW_DASSERT(ElementNonBlockingMightFail::Acquire().ok()); }
216
218 void Release() { PW_DASSERT(ElementNonBlockingMightFail::Release().ok()); }
219};
220
237template <typename ElementType>
238class ClockSource : public ElementType {
239 private:
244 if (this->IncRef() > 1) {
245 // This clock tree element is already enabled.
246 return OkStatus();
247 }
248
249 // Enable clock source.
250 Status status = this->DoEnable();
251 if (!status.ok()) {
252 this->DecRef();
253 }
254 return status;
255 }
256
261 if (this->DecRef() > 0) {
262 // The clock tree element remains enabled.
263 return OkStatus();
264 }
265
266 // Disable the clock source.
267 Status status = this->DoDisable();
268 if (!status.ok()) {
269 this->IncRef();
270 }
271 return status;
272 }
273};
274
278class ClockSourceNoOp : public ClockSource<ElementNonBlockingCannotFail> {
279 private:
280 pw::Status DoEnable() final { return pw::OkStatus(); }
281
282 pw::Status DoDisable() final { return pw::OkStatus(); }
283};
284
298template <typename ElementType>
299class DependentElement : public ElementType {
300 public:
302 constexpr DependentElement(ElementType& source) : source_(&source) {}
303
304 private:
311 if (this->IncRef() > 1) {
312 // This clock tree element is already enabled.
313 return OkStatus();
314 }
315
316 // Acquire a reference to the dependent clock tree element before
317 // enabling this clock tree element.
318 if (Status status = source_->Acquire(); !status.ok()) {
319 this->DecRef();
320 return status;
321 }
322
323 Status status = this->DoEnable();
324 if (!status.ok()) {
325 source_->Release().IgnoreError();
326 this->DecRef();
327 }
328 return status;
329 }
330
337 if (this->DecRef() > 0) {
338 // The clock tree element remains enabled.
339 return OkStatus();
340 }
341
342 // Disable the clock tree element.
343 if (Status status = this->DoDisable(); !status.ok()) {
344 this->IncRef();
345 return status;
346 }
347 // Even if releasing the dependent source references fails,
348 // we won't re-enable the clock source, and instead just return
349 // the error code to the caller.
350 return source_->Release();
351 }
352
353 private:
354 // NOTE: This is an Element* not ElementType* to allow the Acquire() and
355 // Release() calls above to consistently use a Status return type.
356 // We still use ElementType& in the constructor to ensure a nonblocking clock
357 // element cannot depend on a blocking one.
358 // TODO: https://pwbug.dev/434801926 - Change this this to ElementType and
359 // use something like:
360 // if constexpr (std::remove_pointer_t<decltype(source_)>::kMayFail)
361 // to handle the optional Status return value.
362 Element* source_;
363};
364
372 public:
373 constexpr ClockDivider(Element& element) : element_(element) {}
374
375 virtual ~ClockDivider() = default;
376
382 Status SetDivider(uint32_t divider) { return DoSetDivider(divider); }
383
384 // TODO: https://pwbug.dev/434801926 - Remove this compat shim
385 [[deprecated("Use SetDivider()")]]
386 Status Set(uint32_t divider) {
387 return SetDivider(divider);
388 }
389
391 Element& element() const { return element_; }
392
393 private:
395 Element& element_;
396
397 virtual Status DoSetDivider(uint32_t divider) = 0;
398};
399
412template <typename ElementType>
413class ClockDividerElement : public DependentElement<ElementType>,
414 public ClockDivider {
415 public:
418 constexpr ClockDividerElement(ElementType& source, uint32_t divider)
419 : DependentElement<ElementType>(source),
420 ClockDivider(static_cast<Element&>(*this)),
421 divider_(divider) {}
422
428 template <typename T = ElementType>
429 typename std::enable_if_t<T::kMayFail, pw::Status> SetDivider(
430 uint32_t divider) {
432 }
433
439 template <typename T = ElementType>
440 typename std::enable_if_t<!T::kMayFail, void> SetDivider(uint32_t divider) {
441 PW_ASSERT_OK(ClockDivider::SetDivider(divider));
442 }
443
444 protected:
446 uint32_t divider() const { return divider_; }
447
448 private:
450 uint32_t divider_;
451
452 Status DoSetDivider(uint32_t divider) final {
453 std::lock_guard lock(this->lock());
454 return DoSetDividerLocked(divider);
455 }
456
457 Status DoSetDividerLocked(uint32_t divider) {
458 uint32_t old_divider = divider_;
459 divider_ = divider;
460 if (this->ref_count() == 0) {
461 return OkStatus();
462 }
463
464 Status status = this->DoEnable();
465 if (!status.ok()) {
466 // Restore old divider value.
467 divider_ = old_divider;
468 }
469 return status;
470 }
471};
472
475
480
485
491 public:
492 explicit constexpr OptionalElement(Element* element = nullptr)
493 : element_(element) {}
494
495 explicit constexpr OptionalElement(Element& element)
496 : OptionalElement(&element) {}
497
503 if (element_ != nullptr) {
504 return element_->Acquire();
505 }
506 return OkStatus();
507 }
508
514 if (element_ != nullptr) {
515 return element_->Release();
516 }
517 return OkStatus();
518 }
519
520 private:
521 Element* element_ = nullptr;
522};
523
524} // namespace pw::clock_tree
Definition: status.h:86
constexpr bool ok() const
Definition: status.h:158
constexpr void IgnoreError() const
Definition: status.h:223
Definition: clock_tree.h:414
std::enable_if_t<!T::kMayFail, void > SetDivider(uint32_t divider)
Definition: clock_tree.h:440
Definition: clock_tree.h:371
Definition: clock_tree.h:238
Definition: clock_tree.h:278
Definition: clock_tree.h:299
Definition: clock_tree.h:160
Definition: clock_tree.h:45
Definition: clock_tree.h:490
Definition: interrupt_spin_lock.h:50
Definition: mutex.h:40
Status SetDivider(uint32_t divider)
Definition: clock_tree.h:382
void Release()
Release a reference to this clock tree element.
Definition: clock_tree.h:218
void Acquire()
Acquire a reference to this clock tree element.
Definition: clock_tree.h:215
Status Acquire()
Definition: clock_tree.h:502
Status DoAcquireLocked() final
Definition: clock_tree.h:243
std::enable_if_t< T::kMayFail, pw::Status > SetDivider(uint32_t divider)
Definition: clock_tree.h:429
virtual Status DoAcquireLocked()=0
Status Release()
Definition: clock_tree.h:76
Status DoRelease() final
Handle Release(), deferring locking the child class.
Definition: clock_tree.h:178
virtual Status DoReleaseLocked()=0
Status Acquire()
Definition: clock_tree.h:68
Element & element() const
Return the element implementing this interface.
Definition: clock_tree.h:391
Status DoReleaseLocked() final
Definition: clock_tree.h:260
virtual Status DoEnable()=0
Function called when the clock tree element needs to get enabled.
Status DoAcquire() final
Handle Acquire(), deferring locking the child class.
Definition: clock_tree.h:174
uint32_t IncRef()
Increment reference count and return incremented value.
Definition: clock_tree.h:130
pw::Status DoEnable() final
Function called when the clock tree element needs to get enabled.
Definition: clock_tree.h:280
Status DoAcquire() final
Handle Acquire(), deferring locking the child class.
Definition: clock_tree.h:198
uint32_t divider() const
Get current divider value.
Definition: clock_tree.h:446
bool may_block() const
Check whether acquiring or releasing the element may block.
Definition: clock_tree.h:54
constexpr ClockDividerElement(ElementType &source, uint32_t divider)
Definition: clock_tree.h:418
Status DoRelease() final
Handle Release(), deferring locking the child class.
Definition: clock_tree.h:202
constexpr DependentElement(ElementType &source)
Create a dependent clock tree element that depends on source.
Definition: clock_tree.h:302
virtual Status DoAcquire()=0
Handle Acquire(), deferring locking the child class.
uint32_t ref_count() const
Get reference count for this clock tree element.
Definition: clock_tree.h:51
pw::Status DoDisable() final
Definition: clock_tree.h:282
Status Release()
Definition: clock_tree.h:513
uint32_t DecRef()
Decrement reference count and return decremented value.
Definition: clock_tree.h:133
virtual Status DoRelease()=0
Handle Release(), deferring locking the child class.
Status DoAcquireLocked() final
Definition: clock_tree.h:310
Status AcquireWith(Element &element_with)
Definition: clock_tree.h:89
Status DoReleaseLocked() final
Definition: clock_tree.h:336
virtual Status DoDisable()
Definition: clock_tree.h:142
Clock tree management library.
Definition: clock_tree.h:30
constexpr Status OkStatus()
Definition: status.h:235