blob: 4280ce46f57e3d924013efbef72b3534043e65eb [file] [log] [blame]
Brian Silverman8fce7482020-01-05 13:18:21 -08001/*----------------------------------------------------------------------------*/
Austin Schuh1e69f942020-11-14 15:06:14 -08002/* Copyright (c) 2008-2020 FIRST. All Rights Reserved. */
Brian Silverman8fce7482020-01-05 13:18:21 -08003/* 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 "frc/Notifier.h"
9
10#include <utility>
11
12#include <hal/FRCUsageReporting.h>
13#include <hal/Notifier.h>
Austin Schuh1e69f942020-11-14 15:06:14 -080014#include <hal/Threads.h>
Brian Silverman8fce7482020-01-05 13:18:21 -080015#include <wpi/SmallString.h>
16
17#include "frc/Timer.h"
18#include "frc/Utility.h"
19#include "frc/WPIErrors.h"
20
21using namespace frc;
22
23Notifier::Notifier(std::function<void()> handler) {
24 if (handler == nullptr)
25 wpi_setWPIErrorWithContext(NullParameter, "handler must not be nullptr");
26 m_handler = handler;
27 int32_t status = 0;
28 m_notifier = HAL_InitializeNotifier(&status);
29 wpi_setHALError(status);
30
31 m_thread = std::thread([=] {
32 for (;;) {
33 int32_t status = 0;
34 HAL_NotifierHandle notifier = m_notifier.load();
35 if (notifier == 0) break;
36 uint64_t curTime = HAL_WaitForNotifierAlarm(notifier, &status);
37 if (curTime == 0 || status != 0) break;
38
39 std::function<void()> handler;
40 {
41 std::scoped_lock lock(m_processMutex);
42 handler = m_handler;
43 if (m_periodic) {
44 m_expirationTime += m_period;
45 UpdateAlarm();
46 } else {
47 // need to update the alarm to cause it to wait again
48 UpdateAlarm(UINT64_MAX);
49 }
50 }
51
52 // call callback
53 if (handler) handler();
54 }
55 });
56}
57
Austin Schuh1e69f942020-11-14 15:06:14 -080058Notifier::Notifier(int priority, std::function<void()> handler) {
59 if (handler == nullptr)
60 wpi_setWPIErrorWithContext(NullParameter, "handler must not be nullptr");
61 m_handler = handler;
62 int32_t status = 0;
63 m_notifier = HAL_InitializeNotifier(&status);
64 wpi_setHALError(status);
65
66 m_thread = std::thread([=] {
67 int32_t status = 0;
68 HAL_SetCurrentThreadPriority(true, priority, &status);
69 for (;;) {
70 HAL_NotifierHandle notifier = m_notifier.load();
71 if (notifier == 0) break;
72 uint64_t curTime = HAL_WaitForNotifierAlarm(notifier, &status);
73 if (curTime == 0 || status != 0) break;
74
75 std::function<void()> handler;
76 {
77 std::scoped_lock lock(m_processMutex);
78 handler = m_handler;
79 if (m_periodic) {
80 m_expirationTime += m_period;
81 UpdateAlarm();
82 } else {
83 // need to update the alarm to cause it to wait again
84 UpdateAlarm(UINT64_MAX);
85 }
86 }
87
88 // call callback
89 if (handler) handler();
90 }
91 });
92}
93
Brian Silverman8fce7482020-01-05 13:18:21 -080094Notifier::~Notifier() {
95 int32_t status = 0;
96 // atomically set handle to 0, then clean
97 HAL_NotifierHandle handle = m_notifier.exchange(0);
98 HAL_StopNotifier(handle, &status);
99 wpi_setHALError(status);
100
101 // Join the thread to ensure the handler has exited.
102 if (m_thread.joinable()) m_thread.join();
103
104 HAL_CleanNotifier(handle, &status);
105}
106
107Notifier::Notifier(Notifier&& rhs)
108 : ErrorBase(std::move(rhs)),
109 m_thread(std::move(rhs.m_thread)),
110 m_notifier(rhs.m_notifier.load()),
111 m_handler(std::move(rhs.m_handler)),
112 m_expirationTime(std::move(rhs.m_expirationTime)),
113 m_period(std::move(rhs.m_period)),
114 m_periodic(std::move(rhs.m_periodic)) {
115 rhs.m_notifier = HAL_kInvalidHandle;
116}
117
118Notifier& Notifier::operator=(Notifier&& rhs) {
119 ErrorBase::operator=(std::move(rhs));
120
121 m_thread = std::move(rhs.m_thread);
122 m_notifier = rhs.m_notifier.load();
123 rhs.m_notifier = HAL_kInvalidHandle;
124 m_handler = std::move(rhs.m_handler);
125 m_expirationTime = std::move(rhs.m_expirationTime);
126 m_period = std::move(rhs.m_period);
127 m_periodic = std::move(rhs.m_periodic);
128
129 return *this;
130}
131
132void Notifier::SetName(const wpi::Twine& name) {
133 wpi::SmallString<64> nameBuf;
134 int32_t status = 0;
135 HAL_SetNotifierName(m_notifier,
136 name.toNullTerminatedStringRef(nameBuf).data(), &status);
137}
138
139void Notifier::SetHandler(std::function<void()> handler) {
140 std::scoped_lock lock(m_processMutex);
141 m_handler = handler;
142}
143
144void Notifier::StartSingle(double delay) {
145 StartSingle(units::second_t(delay));
146}
147
148void Notifier::StartSingle(units::second_t delay) {
149 std::scoped_lock lock(m_processMutex);
150 m_periodic = false;
151 m_period = delay.to<double>();
152 m_expirationTime = Timer::GetFPGATimestamp() + m_period;
153 UpdateAlarm();
154}
155
156void Notifier::StartPeriodic(double period) {
157 StartPeriodic(units::second_t(period));
158}
159
160void Notifier::StartPeriodic(units::second_t period) {
161 std::scoped_lock lock(m_processMutex);
162 m_periodic = true;
163 m_period = period.to<double>();
164 m_expirationTime = Timer::GetFPGATimestamp() + m_period;
165 UpdateAlarm();
166}
167
168void Notifier::Stop() {
Austin Schuh1e69f942020-11-14 15:06:14 -0800169 std::scoped_lock lock(m_processMutex);
170 m_periodic = false;
Brian Silverman8fce7482020-01-05 13:18:21 -0800171 int32_t status = 0;
172 HAL_CancelNotifierAlarm(m_notifier, &status);
173 wpi_setHALError(status);
174}
175
176void Notifier::UpdateAlarm(uint64_t triggerTime) {
177 int32_t status = 0;
178 // Return if we are being destructed, or were not created successfully
179 auto notifier = m_notifier.load();
180 if (notifier == 0) return;
181 HAL_UpdateNotifierAlarm(notifier, triggerTime, &status);
182 wpi_setHALError(status);
183}
184
185void Notifier::UpdateAlarm() {
186 UpdateAlarm(static_cast<uint64_t>(m_expirationTime * 1e6));
187}