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
388 Element& element() const { return element_; }
389
390 private:
392 Element& element_;
393
394 virtual Status DoSetDivider(uint32_t divider) = 0;
395};
396
409template <typename ElementType>
410class ClockDividerElement : public DependentElement<ElementType>,
411 public ClockDivider {
412 public:
415 constexpr ClockDividerElement(ElementType& source, uint32_t divider)
416 : DependentElement<ElementType>(source),
417 ClockDivider(static_cast<Element&>(*this)),
418 divider_(divider) {}
419
425 template <typename T = ElementType>
426 typename std::enable_if_t<T::kMayFail, pw::Status> SetDivider(
427 uint32_t divider) {
429 }
430
436 template <typename T = ElementType>
437 typename std::enable_if_t<!T::kMayFail, void> SetDivider(uint32_t divider) {
438 PW_ASSERT_OK(ClockDivider::SetDivider(divider));
439 }
440
441 protected:
443 uint32_t divider() const { return divider_; }
444
445 private:
447 uint32_t divider_;
448
449 Status DoSetDivider(uint32_t divider) final {
450 std::lock_guard lock(this->lock());
451 return DoSetDividerLocked(divider);
452 }
453
454 Status DoSetDividerLocked(uint32_t divider) {
455 uint32_t old_divider = divider_;
456 divider_ = divider;
457 if (this->ref_count() == 0) {
458 return OkStatus();
459 }
460
461 Status status = this->DoEnable();
462 if (!status.ok()) {
463 // Restore old divider value.
464 divider_ = old_divider;
465 }
466 return status;
467 }
468};
469
472
477
482
488 public:
489 explicit constexpr OptionalElement(Element* element = nullptr)
490 : element_(element) {}
491
492 explicit constexpr OptionalElement(Element& element)
493 : OptionalElement(&element) {}
494
500 if (element_ != nullptr) {
501 return element_->Acquire();
502 }
503 return OkStatus();
504 }
505
511 if (element_ != nullptr) {
512 return element_->Release();
513 }
514 return OkStatus();
515 }
516
517 private:
518 Element* element_ = nullptr;
519};
520
521} // 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:411
std::enable_if_t<!T::kMayFail, void > SetDivider(uint32_t divider)
Definition: clock_tree.h:437
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:487
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:499
Status DoAcquireLocked() final
Definition: clock_tree.h:246
std::enable_if_t< T::kMayFail, pw::Status > SetDivider(uint32_t divider)
Definition: clock_tree.h:426
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:388
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:443
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:415
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:510
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