aboutsummaryrefslogtreecommitdiff
path: root/pw_chrono/public/pw_chrono/system_timer.h
blob: f356edecc664523dc221b9cda8c3da259857ca21 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
// Copyright 2021 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#pragma once

#include "pw_chrono/system_clock.h"
#include "pw_chrono_backend/system_timer_native.h"
#include "pw_function/function.h"

namespace pw::chrono {

// The SystemTimer allows an ExpiryCallback be executed at a set time in the
// future.
//
// The base SystemTimer only supports a one-shot style timer with a callback.
// A periodic timer can be implemented by rescheduling the timer in the callback
// through InvokeAt(kDesiredPeriod + expired_deadline).
//
// When implementing a periodic layer on top, the user should be mindful of
// handling missed periodic callbacks. They could opt to invoke the callback
// multiple times with the expected expired_deadline values or instead
// saturate and invoke the callback only once with the latest expired_deadline.
//
// The entire API is thread safe, however it is NOT always IRQ safe.
class SystemTimer {
 public:
  using native_handle_type = backend::NativeSystemTimerHandle;

  // The ExpiryCallback is either invoked from a high priority thread or an
  // interrupt.
  //
  // For a given timer instance, its ExpiryCallback will not preempt itself.
  // This makes it appear like there is a single executor of a timer instance's
  // ExpiryCallback.
  //
  // Ergo ExpiryCallbacks should be treated as if they are executed by an
  // interrupt, meaning:
  // - Processing inside of the callback should be kept to a minimum.
  // - Callbacks should never attempt to block.
  // - APIs which are not interrupt safe such as pw::sync::Mutex should not be
  //   used!
  using ExpiryCallback =
      Function<void(SystemClock::time_point expired_deadline)>;

  SystemTimer(ExpiryCallback&& callback);

  // Cancels the timer and blocks if necssary if the callback is already being
  // processed.
  //
  // Postcondition: The expiry callback is not in progress and will not be
  // called in the future.
  ~SystemTimer();

  SystemTimer(const SystemTimer&) = delete;
  SystemTimer(SystemTimer&&) = delete;
  SystemTimer& operator=(const SystemTimer&) = delete;
  SystemTimer& operator=(SystemTimer&&) = delete;

  // Invokes the expiry callback as soon as possible after at least the
  // specified duration.
  //
  // Scheduling a callback cancels the existing callback (if pending).
  // If the callback is already being executed while you reschedule it, it will
  // finish callback execution to completion. You are responsible for any
  // critical section locks which may be needed for timer coordination.
  //
  // This is thread safe, it may not be IRQ safe.
  void InvokeAfter(SystemClock::duration delay);

  // Invokes the expiry callback as soon as possible starting at the specified
  // time_point.
  //
  // Scheduling a callback cancels the existing callback (if pending).
  // If the callback is already being executed while you reschedule it, it will
  // finish callback execution to completion. You are responsible for any
  // critical section locks which may be needed for timer coordination.
  //
  // This is thread safe, it may not be IRQ safe.
  void InvokeAt(SystemClock::time_point timestamp);

  // Cancels the software timer expiry callback if pending.
  //
  // Canceling a timer which isn't scheduled does nothing.
  //
  // If the callback is already being executed while you cancel it, it will
  // finish callback execution to completion. You are responsible for any
  // synchronization which is needed for thread safety.
  //
  // This is thread safe, it may not be IRQ safe.
  void Cancel();

  native_handle_type native_handle();

 private:
  // This may be a wrapper around a native type with additional members.
  backend::NativeSystemTimer native_type_;
};

}  // namespace pw::chrono

#include "pw_chrono_backend/system_timer_inline.h"