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:
50 static constexpr bool kMayBlock = true;
51 static constexpr bool kMayFail = true;
52
53 constexpr Element() {}
54 virtual ~Element() = default;
55
57 uint32_t ref_count() const { return ref_count_; }
58
59 // Not copyable or movable
60 Element(const Element&) = delete;
61 Element(const Element&&) = delete;
62 Element& operator=(const Element&) = delete;
63 Element& operator=(const Element&&) = delete;
64
71 Status Acquire() { return DoAcquire(); }
72
79 Status Release() { return DoRelease(); }
80
92 Status AcquireWith(Element& element_with) {
93 PW_TRY(element_with.Acquire());
94 Status status = Acquire();
95 // Ignore any error on release because it should not fail if the acquire
96 // succeeded, and there's no reasonable alternative.
97 element_with.Release().IgnoreError();
98 return status;
99 }
100
101 protected:
113 virtual Status DoAcquireLocked() = 0;
114
130 virtual Status DoReleaseLocked() = 0;
131
133 uint32_t IncRef() { return ++ref_count_; }
134
136 uint32_t DecRef() {
137 PW_ASSERT(ref_count_ > 0);
138 return --ref_count_;
139 }
140
142 virtual Status DoEnable() = 0;
143
148 virtual Status DoDisable() { return OkStatus(); }
149
150 private:
152 uint32_t ref_count_ = 0;
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
168 protected:
169 sync::Mutex& lock() { return *mutex_; }
170
171 private:
172 // sync::Mutex is not constexpr-constructible so use DeferredInit so we can
173 // keep our constexpr constructor.
174 internal::DeferredInit<sync::Mutex> mutex_;
175
177 std::lock_guard lock(this->lock());
178 return DoAcquireLocked();
179 }
181 std::lock_guard lock(this->lock());
182 return DoReleaseLocked();
183 }
184};
185
189 public:
190 static constexpr bool kMayBlock = false;
191 static constexpr bool kMayFail = true;
192
193 protected:
194 sync::InterruptSpinLock& lock() { return spin_lock_; }
195
196 private:
197 sync::InterruptSpinLock spin_lock_;
198
200 std::lock_guard lock(this->lock());
201 return DoAcquireLocked();
202 }
204 std::lock_guard lock(this->lock());
205 return DoReleaseLocked();
206 }
207};
208
212 public:
213 static constexpr bool kMayFail = false;
214
216 void Acquire() { PW_DASSERT(ElementNonBlockingMightFail::Acquire().ok()); }
217
219 void Release() { PW_DASSERT(ElementNonBlockingMightFail::Release().ok()); }
220};
221
238template <typename ElementType>
239class ClockSource : public ElementType {
240 private:
245 if (this->IncRef() > 1) {
246 // This clock tree element is already enabled.
247 return OkStatus();
248 }
249
250 // Enable clock source.
251 Status status = this->DoEnable();
252 if (!status.ok()) {
253 this->DecRef();
254 }
255 return status;
256 }
257
262 if (this->DecRef() > 0) {
263 // The clock tree element remains enabled.
264 return OkStatus();
265 }
266
267 // Disable the clock source.
268 Status status = this->DoDisable();
269 if (!status.ok()) {
270 this->IncRef();
271 }
272 return status;
273 }
274};
275
278
282
286class ClockSourceNoOp : public ClockSource<ElementNonBlockingCannotFail> {
287 private:
288 pw::Status DoEnable() final { return pw::OkStatus(); }
289
290 pw::Status DoDisable() final { return pw::OkStatus(); }
291};
292
296class ClockSourceNoOpBlocking : public ClockSource<ElementBlocking> {
297 private:
298 pw::Status DoEnable() final { return pw::OkStatus(); }
299
300 pw::Status DoDisable() final { return pw::OkStatus(); }
301};
302
304using ClockSourceBlocking = ClockSource<ElementBlocking>;
305
310
315
337template <typename ElementType>
338class DependentElement : public ElementType {
339 public:
341 template <typename SourceType>
342 constexpr DependentElement(SourceType& source) : source_(&source) {
343 static_assert(ElementType::kMayBlock || !SourceType::kMayBlock,
344 "Non-blocking element cannot depend on a blocking element");
345 static_assert(
346 ElementType::kMayFail || !SourceType::kMayFail,
347 "Non-failing element cannot depend on an element that might fail");
348 }
349
350 private:
357 if (this->IncRef() > 1) {
358 // This clock tree element is already enabled.
359 return OkStatus();
360 }
361
362 // Acquire a reference to the dependent clock tree element before
363 // enabling this clock tree element.
364 if (Status status = source_->Acquire(); !status.ok()) {
365 this->DecRef();
366 return status;
367 }
368
369 Status status = this->DoEnable();
370 if (!status.ok()) {
371 source_->Release().IgnoreError();
372 this->DecRef();
373 }
374 return status;
375 }
376
383 if (this->DecRef() > 0) {
384 // The clock tree element remains enabled.
385 return OkStatus();
386 }
387
388 // Disable the clock tree element.
389 if (Status status = this->DoDisable(); !status.ok()) {
390 this->IncRef();
391 return status;
392 }
393 // Even if releasing the dependent source references fails,
394 // we won't re-enable the clock source, and instead just return
395 // the error code to the caller.
396 return source_->Release();
397 }
398
399 protected:
404 template <typename SourceType>
405 void SetSource(SourceType& source) {
406 static_assert(ElementType::kMayBlock || !SourceType::kMayBlock,
407 "Non-blocking element cannot depend on a blocking element");
408 static_assert(
409 ElementType::kMayFail || !SourceType::kMayFail,
410 "Non-failing element cannot depend on an element that might fail");
411 source_ = &source;
412 }
413
414 Element& source() { return *source_; }
415
416 private:
417 // NOTE: This is an Element* not SourceType* to allow the source to be
418 // changed at runtime.
419 // We still use ElementType and SourceType& in the constructor to ensure a
420 // nonblocking clock element cannot depend on a blocking one.
421 // TODO: https://pwbug.dev/434801926 - Change this this to ElementType and
422 // use something like:
423 // if constexpr (std::remove_pointer_t<decltype(source_)>::kMayFail)
424 // to handle the optional Status return value.
425 Element* source_;
426};
427
435 public:
436 constexpr ClockDivider(Element& element) : element_(element) {}
437
438 virtual ~ClockDivider() = default;
439
445 Status SetDivider(uint32_t divider) { return DoSetDivider(divider); }
446
448 Element& element() const { return element_; }
449
450 private:
452 Element& element_;
453
454 virtual Status DoSetDivider(uint32_t divider) = 0;
455};
456
469template <typename ElementType>
470class ClockDividerElement : public DependentElement<ElementType>,
471 public ClockDivider {
472 public:
475 template <typename SourceType>
476 constexpr ClockDividerElement(SourceType& source, uint32_t divider)
477 : DependentElement<ElementType>(source),
478 ClockDivider(static_cast<Element&>(*this)),
479 divider_(divider) {}
480
486 template <typename T = ElementType>
487 typename std::enable_if_t<T::kMayFail, pw::Status> SetDivider(
488 uint32_t divider) {
490 }
491
497 template <typename T = ElementType>
498 typename std::enable_if_t<!T::kMayFail, void> SetDivider(uint32_t divider) {
499 PW_ASSERT_OK(ClockDivider::SetDivider(divider));
500 }
501
502 protected:
504 uint32_t divider() const { return divider_; }
505
506 private:
508 uint32_t divider_;
509
510 Status DoSetDivider(uint32_t divider) final {
511 std::lock_guard lock(this->lock());
512 return DoSetDividerLocked(divider);
513 }
514
515 Status DoSetDividerLocked(uint32_t divider) {
516 uint32_t old_divider = divider_;
517 divider_ = divider;
518 if (this->ref_count() == 0) {
519 return OkStatus();
520 }
521
522 Status status = this->DoEnable();
523 if (!status.ok()) {
524 // Restore old divider value.
525 divider_ = old_divider;
526 }
527 return status;
528 }
529};
530
533
538
543
549 public:
550 explicit constexpr OptionalElement(Element* element = nullptr)
551 : element_(element) {}
552
553 explicit constexpr OptionalElement(Element& element)
554 : OptionalElement(&element) {}
555
561 if (element_ != nullptr) {
562 return element_->Acquire();
563 }
564 return OkStatus();
565 }
566
572 if (element_ != nullptr) {
573 return element_->Release();
574 }
575 return OkStatus();
576 }
577
578 private:
579 Element* element_ = nullptr;
580};
581
582} // 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:471
std::enable_if_t<!T::kMayFail, void > SetDivider(uint32_t divider)
Definition: clock_tree.h:498
Definition: clock_tree.h:434
Definition: clock_tree.h:239
Definition: clock_tree.h:296
Definition: clock_tree.h:286
Definition: clock_tree.h:338
Definition: clock_tree.h:163
Definition: clock_tree.h:45
Definition: clock_tree.h:548
Definition: interrupt_spin_lock.h:50
Definition: mutex.h:40
Status SetDivider(uint32_t divider)
Definition: clock_tree.h:445
pw::Status DoEnable() final
Function called when the clock tree element needs to get enabled.
Definition: clock_tree.h:298
void Release()
Release a reference to this clock tree element.
Definition: clock_tree.h:219
void Acquire()
Acquire a reference to this clock tree element.
Definition: clock_tree.h:216
pw::Status DoDisable() final
Definition: clock_tree.h:300
Status Acquire()
Definition: clock_tree.h:560
constexpr DependentElement(SourceType &source)
Create a dependent clock tree element that depends on source.
Definition: clock_tree.h:342
ClockSource< ElementBlocking > ClockSourceBlocking
Alias for a blocking clock source tree element.
Definition: clock_tree.h:277
Status DoAcquireLocked() final
Definition: clock_tree.h:244
std::enable_if_t< T::kMayFail, pw::Status > SetDivider(uint32_t divider)
Definition: clock_tree.h:487
virtual Status DoAcquireLocked()=0
Status Release()
Definition: clock_tree.h:79
static constexpr bool kMayBlock
Definition: clock_tree.h:50
Status DoRelease() final
Handle Release(), deferring locking the child class.
Definition: clock_tree.h:180
virtual Status DoReleaseLocked()=0
Status Acquire()
Definition: clock_tree.h:71
Element & element() const
Return the element implementing this interface.
Definition: clock_tree.h:448
Status DoReleaseLocked() final
Definition: clock_tree.h:261
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:176
uint32_t IncRef()
Increment reference count and return incremented value.
Definition: clock_tree.h:133
pw::Status DoEnable() final
Function called when the clock tree element needs to get enabled.
Definition: clock_tree.h:288
void SetSource(SourceType &source)
Definition: clock_tree.h:405
constexpr ClockDividerElement(SourceType &source, uint32_t divider)
Definition: clock_tree.h:476
Status DoAcquire() final
Handle Acquire(), deferring locking the child class.
Definition: clock_tree.h:199
uint32_t divider() const
Get current divider value.
Definition: clock_tree.h:504
Status DoRelease() final
Handle Release(), deferring locking the child class.
Definition: clock_tree.h:203
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:57
pw::Status DoDisable() final
Definition: clock_tree.h:290
Status Release()
Definition: clock_tree.h:571
uint32_t DecRef()
Decrement reference count and return decremented value.
Definition: clock_tree.h:136
virtual Status DoRelease()=0
Handle Release(), deferring locking the child class.
Status DoAcquireLocked() final
Definition: clock_tree.h:356
Status AcquireWith(Element &element_with)
Definition: clock_tree.h:92
Status DoReleaseLocked() final
Definition: clock_tree.h:382
virtual Status DoDisable()
Definition: clock_tree.h:148
#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