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() {
134 PW_ASSERT(ref_count_ > 0);
135 return --ref_count_;
136 }
137
139 virtual Status DoEnable() = 0;
140
145 virtual Status DoDisable() { return OkStatus(); }
146
147 private:
149 uint32_t ref_count_ = 0;
150
152 const bool may_block_;
153
155 virtual Status DoAcquire() = 0;
156
158 virtual Status DoRelease() = 0;
159};
160
163class ElementBlocking : public Element {
164 public:
165 static constexpr bool kMayBlock = true;
166 static constexpr bool kMayFail = true;
167 constexpr ElementBlocking() : Element(kMayBlock) {}
168
169 protected:
170 sync::Mutex& lock() { return *mutex_; }
171
172 private:
173 // sync::Mutex is not constexpr-constructible so use DeferredInit so we can
174 // keep our constexpr constructor.
175 internal::DeferredInit<sync::Mutex> mutex_;
176
178 std::lock_guard lock(this->lock());
179 return DoAcquireLocked();
180 }
182 std::lock_guard lock(this->lock());
183 return DoReleaseLocked();
184 }
185};
186
190 public:
191 static constexpr bool kMayBlock = false;
192 static constexpr bool kMayFail = true;
193 constexpr ElementNonBlockingMightFail() : Element(kMayBlock) {}
194
195 protected:
196 sync::InterruptSpinLock& lock() { return spin_lock_; }
197
198 private:
199 sync::InterruptSpinLock spin_lock_;
200
202 std::lock_guard lock(this->lock());
203 return DoAcquireLocked();
204 }
206 std::lock_guard lock(this->lock());
207 return DoReleaseLocked();
208 }
209};
210
214 public:
215 static constexpr bool kMayFail = false;
216
218 void Acquire() { PW_DASSERT(ElementNonBlockingMightFail::Acquire().ok()); }
219
221 void Release() { PW_DASSERT(ElementNonBlockingMightFail::Release().ok()); }
222};
223
240template <typename ElementType>
241class ClockSource : public ElementType {
242 private:
247 if (this->IncRef() > 1) {
248 // This clock tree element is already enabled.
249 return OkStatus();
250 }
251
252 // Enable clock source.
253 Status status = this->DoEnable();
254 if (!status.ok()) {
255 this->DecRef();
256 }
257 return status;
258 }
259
264 if (this->DecRef() > 0) {
265 // The clock tree element remains enabled.
266 return OkStatus();
267 }
268
269 // Disable the clock source.
270 Status status = this->DoDisable();
271 if (!status.ok()) {
272 this->IncRef();
273 }
274 return status;
275 }
276};
277
281class ClockSourceNoOp : public ClockSource<ElementNonBlockingCannotFail> {
282 private:
283 pw::Status DoEnable() final { return pw::OkStatus(); }
284
285 pw::Status DoDisable() final { return pw::OkStatus(); }
286};
287
301template <typename ElementType>
302class DependentElement : public ElementType {
303 public:
305 constexpr DependentElement(ElementType& source) : source_(&source) {}
306
307 private:
314 if (this->IncRef() > 1) {
315 // This clock tree element is already enabled.
316 return OkStatus();
317 }
318
319 // Acquire a reference to the dependent clock tree element before
320 // enabling this clock tree element.
321 if (Status status = source_->Acquire(); !status.ok()) {
322 this->DecRef();
323 return status;
324 }
325
326 Status status = this->DoEnable();
327 if (!status.ok()) {
328 source_->Release().IgnoreError();
329 this->DecRef();
330 }
331 return status;
332 }
333
340 if (this->DecRef() > 0) {
341 // The clock tree element remains enabled.
342 return OkStatus();
343 }
344
345 // Disable the clock tree element.
346 if (Status status = this->DoDisable(); !status.ok()) {
347 this->IncRef();
348 return status;
349 }
350 // Even if releasing the dependent source references fails,
351 // we won't re-enable the clock source, and instead just return
352 // the error code to the caller.
353 return source_->Release();
354 }
355
356 private:
357 // NOTE: This is an Element* not ElementType* to allow the Acquire() and
358 // Release() calls above to consistently use a Status return type.
359 // We still use ElementType& in the constructor to ensure a nonblocking clock
360 // element cannot depend on a blocking one.
361 // TODO: https://pwbug.dev/434801926 - Change this this to ElementType and
362 // use something like:
363 // if constexpr (std::remove_pointer_t<decltype(source_)>::kMayFail)
364 // to handle the optional Status return value.
365 Element* source_;
366};
367
375 public:
376 constexpr ClockDivider(Element& element) : element_(element) {}
377
378 virtual ~ClockDivider() = default;
379
385 Status SetDivider(uint32_t divider) { return DoSetDivider(divider); }
386
387 // TODO: https://pwbug.dev/434801926 - Remove this compat shim
388 [[deprecated("Use SetDivider()")]]
389 Status Set(uint32_t divider) {
390 return SetDivider(divider);
391 }
392
394 Element& element() const { return element_; }
395
396 private:
398 Element& element_;
399
400 virtual Status DoSetDivider(uint32_t divider) = 0;
401};
402
415template <typename ElementType>
416class ClockDividerElement : public DependentElement<ElementType>,
417 public ClockDivider {
418 public:
421 constexpr ClockDividerElement(ElementType& source, uint32_t divider)
422 : DependentElement<ElementType>(source),
423 ClockDivider(static_cast<Element&>(*this)),
424 divider_(divider) {}
425
431 template <typename T = ElementType>
432 typename std::enable_if_t<T::kMayFail, pw::Status> SetDivider(
433 uint32_t divider) {
435 }
436
442 template <typename T = ElementType>
443 typename std::enable_if_t<!T::kMayFail, void> SetDivider(uint32_t divider) {
444 PW_ASSERT_OK(ClockDivider::SetDivider(divider));
445 }
446
447 protected:
449 uint32_t divider() const { return divider_; }
450
451 private:
453 uint32_t divider_;
454
455 Status DoSetDivider(uint32_t divider) final {
456 std::lock_guard lock(this->lock());
457 return DoSetDividerLocked(divider);
458 }
459
460 Status DoSetDividerLocked(uint32_t divider) {
461 uint32_t old_divider = divider_;
462 divider_ = divider;
463 if (this->ref_count() == 0) {
464 return OkStatus();
465 }
466
467 Status status = this->DoEnable();
468 if (!status.ok()) {
469 // Restore old divider value.
470 divider_ = old_divider;
471 }
472 return status;
473 }
474};
475
478
483
488
494 public:
495 explicit constexpr OptionalElement(Element* element = nullptr)
496 : element_(element) {}
497
498 explicit constexpr OptionalElement(Element& element)
499 : OptionalElement(&element) {}
500
506 if (element_ != nullptr) {
507 return element_->Acquire();
508 }
509 return OkStatus();
510 }
511
517 if (element_ != nullptr) {
518 return element_->Release();
519 }
520 return OkStatus();
521 }
522
523 private:
524 Element* element_ = nullptr;
525};
526
527} // namespace pw::clock_tree
Definition: status.h:120
constexpr bool ok() const
Definition: status.h:346
constexpr void IgnoreError() const
Definition: status.h:430
Definition: clock_tree.h:417
std::enable_if_t<!T::kMayFail, void > SetDivider(uint32_t divider)
Definition: clock_tree.h:443
Definition: clock_tree.h:374
Definition: clock_tree.h:241
Definition: clock_tree.h:281
Definition: clock_tree.h:302
Definition: clock_tree.h:163
Definition: clock_tree.h:45
Definition: clock_tree.h:493
Definition: interrupt_spin_lock.h:50
Definition: mutex.h:40
Status SetDivider(uint32_t divider)
Definition: clock_tree.h:385
void Release()
Release a reference to this clock tree element.
Definition: clock_tree.h:221
void Acquire()
Acquire a reference to this clock tree element.
Definition: clock_tree.h:218
Status Acquire()
Definition: clock_tree.h:505
Status DoAcquireLocked() final
Definition: clock_tree.h:246
std::enable_if_t< T::kMayFail, pw::Status > SetDivider(uint32_t divider)
Definition: clock_tree.h:432
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:181
virtual Status DoReleaseLocked()=0
Status Acquire()
Definition: clock_tree.h:68
Element & element() const
Return the element implementing this interface.
Definition: clock_tree.h:394
Status DoReleaseLocked() final
Definition: clock_tree.h:263
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:177
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:283
Status DoAcquire() final
Handle Acquire(), deferring locking the child class.
Definition: clock_tree.h:201
uint32_t divider() const
Get current divider value.
Definition: clock_tree.h:449
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:421
Status DoRelease() final
Handle Release(), deferring locking the child class.
Definition: clock_tree.h:205
constexpr DependentElement(ElementType &source)
Create a dependent clock tree element that depends on source.
Definition: clock_tree.h:305
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:285
Status Release()
Definition: clock_tree.h:516
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:313
Status AcquireWith(Element &element_with)
Definition: clock_tree.h:89
Status DoReleaseLocked() final
Definition: clock_tree.h:339
virtual Status DoDisable()
Definition: clock_tree.h:145
#define PW_TRY(expr)
Returns early if expr is a non-OK Status or Result.
Definition: try.h:27
constexpr Status OkStatus()
Definition: status.h:450
Clock tree management library.
Definition: clock_tree.h:30