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