blob: 67abe37180c2b1558f1abee24473a376334d02be [file] [log] [blame]
Brian Silvermanf7f267a2017-02-04 16:16:08 -08001/*----------------------------------------------------------------------------*/
2/* Copyright (c) FIRST 2008-2017. All Rights Reserved. */
3/* Open Source Software - may be modified and shared by FRC teams. The code */
4/* must be accompanied by the FIRST BSD license file in the root directory of */
5/* the project. */
6/*----------------------------------------------------------------------------*/
7
8#include "Notifier.h"
9
10#include "HAL/HAL.h"
11#include "Timer.h"
12#include "Utility.h"
13#include "WPIErrors.h"
14
15using namespace frc;
16
17priority_mutex Notifier::m_destructorMutex;
18
19/**
20 * Create a Notifier for timer event notification.
21 *
22 * @param handler The handler is called at the notification time which is set
23 * using StartSingle or StartPeriodic.
24 */
25Notifier::Notifier(TimerEventHandler handler) {
26 if (handler == nullptr)
27 wpi_setWPIErrorWithContext(NullParameter, "handler must not be nullptr");
28 m_handler = handler;
29 int32_t status = 0;
30 m_notifier = HAL_InitializeNotifier(&Notifier::Notify, this, &status);
31 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
32}
33
34/**
35 * Free the resources for a timer event.
36 */
37Notifier::~Notifier() {
38 int32_t status = 0;
39 // atomically set handle to 0, then clean
40 HAL_NotifierHandle handle = m_notifier.exchange(0);
41 HAL_CleanNotifier(handle, &status);
42 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
43
44 /* Acquire the mutex; this makes certain that the handler is not being
45 * executed by the interrupt manager.
46 */
47 std::lock_guard<priority_mutex> lockStatic(Notifier::m_destructorMutex);
48 std::lock_guard<priority_mutex> lock(m_processMutex);
49}
50
51/**
52 * Update the HAL alarm time.
53 */
54void Notifier::UpdateAlarm() {
55 int32_t status = 0;
56 // Return if we are being destructed, or were not created successfully
57 if (m_notifier == 0) return;
58 HAL_UpdateNotifierAlarm(
59 m_notifier, static_cast<uint64_t>(m_expirationTime * 1e6), &status);
60 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
61}
62
63/**
64 * Notify is called by the HAL layer. We simply need to pass it through to
65 * the user handler.
66 */
67void Notifier::Notify(uint64_t currentTimeInt, HAL_NotifierHandle handle) {
68 Notifier* notifier;
69 {
70 // Lock static mutex to grab the notifier param
71 std::lock_guard<priority_mutex> lock(Notifier::m_destructorMutex);
72 int32_t status = 0;
73 auto notifierPointer = HAL_GetNotifierParam(handle, &status);
74 if (notifierPointer == nullptr) return;
75 notifier = static_cast<Notifier*>(notifierPointer);
76 notifier->m_processMutex.lock();
77 }
78
79 if (notifier->m_periodic) {
80 notifier->m_expirationTime += notifier->m_period;
81 notifier->UpdateAlarm();
82 }
83
84 auto handler = notifier->m_handler;
85
86 if (handler) handler();
87 notifier->m_processMutex.unlock();
88}
89
90/**
91 * Register for single event notification.
92 *
93 * A timer event is queued for a single event after the specified delay.
94 *
95 * @param delay Seconds to wait before the handler is called.
96 */
97void Notifier::StartSingle(double delay) {
98 std::lock_guard<priority_mutex> sync(m_processMutex);
99 m_periodic = false;
100 m_period = delay;
101 m_expirationTime = GetClock() + m_period;
102 UpdateAlarm();
103}
104
105/**
106 * Register for periodic event notification.
107 *
108 * A timer event is queued for periodic event notification. Each time the
109 * interrupt occurs, the event will be immediately requeued for the same time
110 * interval.
111 *
112 * @param period Period in seconds to call the handler starting one period
113 * after the call to this method.
114 */
115void Notifier::StartPeriodic(double period) {
116 std::lock_guard<priority_mutex> sync(m_processMutex);
117 m_periodic = true;
118 m_period = period;
119 m_expirationTime = GetClock() + m_period;
120 UpdateAlarm();
121}
122
123/**
124 * Stop timer events from occuring.
125 *
126 * Stop any repeating timer events from occuring. This will also remove any
127 * single notification events from the queue.
128 *
129 * If a timer-based call to the registered handler is in progress, this function
130 * will block until the handler call is complete.
131 */
132void Notifier::Stop() {
133 int32_t status = 0;
134 HAL_StopNotifierAlarm(m_notifier, &status);
135 wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
136
137 // Wait for a currently executing handler to complete before returning from
138 // Stop()
139 std::lock_guard<priority_mutex> lockStatic(Notifier::m_destructorMutex);
140 std::lock_guard<priority_mutex> lock(m_processMutex);
141}