blob: a7fa0381bd74de1cdf1686ff69062368448d89df [file] [log] [blame]
Brian Silverman41cdd3e2019-01-19 19:48:58 -08001/*----------------------------------------------------------------------------*/
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -08002/* Copyright (c) 2008-2019 FIRST. All Rights Reserved. */
Brian Silverman41cdd3e2019-01-19 19:48:58 -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
James Kuszmaul4b81d302019-12-14 20:53:14 -080012#include <hal/FRCUsageReporting.h>
13#include <hal/Notifier.h>
14#include <wpi/SmallString.h>
Brian Silverman41cdd3e2019-01-19 19:48:58 -080015
16#include "frc/Timer.h"
17#include "frc/Utility.h"
18#include "frc/WPIErrors.h"
19
20using namespace frc;
21
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -080022Notifier::Notifier(std::function<void()> handler) {
Brian Silverman41cdd3e2019-01-19 19:48:58 -080023 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);
James Kuszmaul4b81d302019-12-14 20:53:14 -080028 wpi_setHALError(status);
Brian Silverman41cdd3e2019-01-19 19:48:58 -080029
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
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -080038 std::function<void()> handler;
Brian Silverman41cdd3e2019-01-19 19:48:58 -080039 {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -080040 std::scoped_lock lock(m_processMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -080041 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);
James Kuszmaul4b81d302019-12-14 20:53:14 -080062 wpi_setHALError(status);
Brian Silverman41cdd3e2019-01-19 19:48:58 -080063
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
James Kuszmaul4b81d302019-12-14 20:53:14 -080095void 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
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800102void Notifier::SetHandler(std::function<void()> handler) {
103 std::scoped_lock lock(m_processMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800104 m_handler = handler;
105}
106
107void Notifier::StartSingle(double delay) {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800108 StartSingle(units::second_t(delay));
109}
110
111void Notifier::StartSingle(units::second_t delay) {
112 std::scoped_lock lock(m_processMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800113 m_periodic = false;
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800114 m_period = delay.to<double>();
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800115 m_expirationTime = Timer::GetFPGATimestamp() + m_period;
116 UpdateAlarm();
117}
118
119void Notifier::StartPeriodic(double period) {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800120 StartPeriodic(units::second_t(period));
121}
122
123void Notifier::StartPeriodic(units::second_t period) {
124 std::scoped_lock lock(m_processMutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800125 m_periodic = true;
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800126 m_period = period.to<double>();
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800127 m_expirationTime = Timer::GetFPGATimestamp() + m_period;
128 UpdateAlarm();
129}
130
131void Notifier::Stop() {
132 int32_t status = 0;
133 HAL_CancelNotifierAlarm(m_notifier, &status);
James Kuszmaul4b81d302019-12-14 20:53:14 -0800134 wpi_setHALError(status);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800135}
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);
James Kuszmaul4b81d302019-12-14 20:53:14 -0800143 wpi_setHALError(status);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800144}
145
146void Notifier::UpdateAlarm() {
147 UpdateAlarm(static_cast<uint64_t>(m_expirationTime * 1e6));
148}