blob: 441f7507c8d83639c7af6c46ad0f319209269d8f [file] [log] [blame]
Austin Schuh812d0d12021-11-04 20:16:48 -07001// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
Brian Silverman8fce7482020-01-05 13:18:21 -08004
5#include "frc/Notifier.h"
6
7#include <utility>
8
Austin Schuh812d0d12021-11-04 20:16:48 -07009#include <fmt/format.h>
Brian Silverman8fce7482020-01-05 13:18:21 -080010#include <hal/FRCUsageReporting.h>
11#include <hal/Notifier.h>
Austin Schuh1e69f942020-11-14 15:06:14 -080012#include <hal/Threads.h>
Brian Silverman8fce7482020-01-05 13:18:21 -080013
Austin Schuh812d0d12021-11-04 20:16:48 -070014#include "frc/Errors.h"
Brian Silverman8fce7482020-01-05 13:18:21 -080015#include "frc/Timer.h"
Brian Silverman8fce7482020-01-05 13:18:21 -080016
17using namespace frc;
18
19Notifier::Notifier(std::function<void()> handler) {
Austin Schuh812d0d12021-11-04 20:16:48 -070020 if (!handler) {
21 throw FRC_MakeError(err::NullParameter, "{}", "handler");
22 }
Brian Silverman8fce7482020-01-05 13:18:21 -080023 m_handler = handler;
24 int32_t status = 0;
25 m_notifier = HAL_InitializeNotifier(&status);
Austin Schuh812d0d12021-11-04 20:16:48 -070026 FRC_CheckErrorStatus(status, "{}", "InitializeNotifier");
Brian Silverman8fce7482020-01-05 13:18:21 -080027
28 m_thread = std::thread([=] {
29 for (;;) {
30 int32_t status = 0;
31 HAL_NotifierHandle notifier = m_notifier.load();
Austin Schuh812d0d12021-11-04 20:16:48 -070032 if (notifier == 0) {
33 break;
34 }
Brian Silverman8fce7482020-01-05 13:18:21 -080035 uint64_t curTime = HAL_WaitForNotifierAlarm(notifier, &status);
Austin Schuh812d0d12021-11-04 20:16:48 -070036 if (curTime == 0 || status != 0) {
37 break;
38 }
Brian Silverman8fce7482020-01-05 13:18:21 -080039
40 std::function<void()> handler;
41 {
42 std::scoped_lock lock(m_processMutex);
43 handler = m_handler;
44 if (m_periodic) {
45 m_expirationTime += m_period;
46 UpdateAlarm();
47 } else {
48 // need to update the alarm to cause it to wait again
49 UpdateAlarm(UINT64_MAX);
50 }
51 }
52
53 // call callback
Austin Schuh812d0d12021-11-04 20:16:48 -070054 if (handler) {
55 handler();
56 }
Brian Silverman8fce7482020-01-05 13:18:21 -080057 }
58 });
59}
60
Austin Schuh1e69f942020-11-14 15:06:14 -080061Notifier::Notifier(int priority, std::function<void()> handler) {
Austin Schuh812d0d12021-11-04 20:16:48 -070062 if (!handler) {
63 throw FRC_MakeError(err::NullParameter, "{}", "handler");
64 }
Austin Schuh1e69f942020-11-14 15:06:14 -080065 m_handler = handler;
66 int32_t status = 0;
67 m_notifier = HAL_InitializeNotifier(&status);
Austin Schuh812d0d12021-11-04 20:16:48 -070068 FRC_CheckErrorStatus(status, "{}", "InitializeNotifier");
Austin Schuh1e69f942020-11-14 15:06:14 -080069
70 m_thread = std::thread([=] {
71 int32_t status = 0;
72 HAL_SetCurrentThreadPriority(true, priority, &status);
73 for (;;) {
74 HAL_NotifierHandle notifier = m_notifier.load();
Austin Schuh812d0d12021-11-04 20:16:48 -070075 if (notifier == 0) {
76 break;
77 }
Austin Schuh1e69f942020-11-14 15:06:14 -080078 uint64_t curTime = HAL_WaitForNotifierAlarm(notifier, &status);
Austin Schuh812d0d12021-11-04 20:16:48 -070079 if (curTime == 0 || status != 0) {
80 break;
81 }
Austin Schuh1e69f942020-11-14 15:06:14 -080082
83 std::function<void()> handler;
84 {
85 std::scoped_lock lock(m_processMutex);
86 handler = m_handler;
87 if (m_periodic) {
88 m_expirationTime += m_period;
89 UpdateAlarm();
90 } else {
91 // need to update the alarm to cause it to wait again
92 UpdateAlarm(UINT64_MAX);
93 }
94 }
95
96 // call callback
Austin Schuh812d0d12021-11-04 20:16:48 -070097 if (handler) {
98 handler();
99 }
Austin Schuh1e69f942020-11-14 15:06:14 -0800100 }
101 });
102}
103
Brian Silverman8fce7482020-01-05 13:18:21 -0800104Notifier::~Notifier() {
105 int32_t status = 0;
106 // atomically set handle to 0, then clean
107 HAL_NotifierHandle handle = m_notifier.exchange(0);
108 HAL_StopNotifier(handle, &status);
Austin Schuh812d0d12021-11-04 20:16:48 -0700109 FRC_ReportError(status, "{}", "StopNotifier");
Brian Silverman8fce7482020-01-05 13:18:21 -0800110
111 // Join the thread to ensure the handler has exited.
Austin Schuh812d0d12021-11-04 20:16:48 -0700112 if (m_thread.joinable()) {
113 m_thread.join();
114 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800115
116 HAL_CleanNotifier(handle, &status);
117}
118
119Notifier::Notifier(Notifier&& rhs)
Austin Schuh812d0d12021-11-04 20:16:48 -0700120 : m_thread(std::move(rhs.m_thread)),
Brian Silverman8fce7482020-01-05 13:18:21 -0800121 m_notifier(rhs.m_notifier.load()),
122 m_handler(std::move(rhs.m_handler)),
123 m_expirationTime(std::move(rhs.m_expirationTime)),
124 m_period(std::move(rhs.m_period)),
125 m_periodic(std::move(rhs.m_periodic)) {
126 rhs.m_notifier = HAL_kInvalidHandle;
127}
128
129Notifier& Notifier::operator=(Notifier&& rhs) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800130 m_thread = std::move(rhs.m_thread);
131 m_notifier = rhs.m_notifier.load();
132 rhs.m_notifier = HAL_kInvalidHandle;
133 m_handler = std::move(rhs.m_handler);
134 m_expirationTime = std::move(rhs.m_expirationTime);
135 m_period = std::move(rhs.m_period);
136 m_periodic = std::move(rhs.m_periodic);
137
138 return *this;
139}
140
Austin Schuh812d0d12021-11-04 20:16:48 -0700141void Notifier::SetName(std::string_view name) {
142 fmt::memory_buffer buf;
143 fmt::format_to(fmt::appender{buf}, "{}", name);
144 buf.push_back('\0'); // null terminate
Brian Silverman8fce7482020-01-05 13:18:21 -0800145 int32_t status = 0;
Austin Schuh812d0d12021-11-04 20:16:48 -0700146 HAL_SetNotifierName(m_notifier, buf.data(), &status);
Brian Silverman8fce7482020-01-05 13:18:21 -0800147}
148
149void Notifier::SetHandler(std::function<void()> handler) {
150 std::scoped_lock lock(m_processMutex);
151 m_handler = handler;
152}
153
Brian Silverman8fce7482020-01-05 13:18:21 -0800154void Notifier::StartSingle(units::second_t delay) {
155 std::scoped_lock lock(m_processMutex);
156 m_periodic = false;
Austin Schuh812d0d12021-11-04 20:16:48 -0700157 m_period = delay;
Brian Silverman8fce7482020-01-05 13:18:21 -0800158 m_expirationTime = Timer::GetFPGATimestamp() + m_period;
159 UpdateAlarm();
160}
161
Brian Silverman8fce7482020-01-05 13:18:21 -0800162void Notifier::StartPeriodic(units::second_t period) {
163 std::scoped_lock lock(m_processMutex);
164 m_periodic = true;
Austin Schuh812d0d12021-11-04 20:16:48 -0700165 m_period = period;
Brian Silverman8fce7482020-01-05 13:18:21 -0800166 m_expirationTime = Timer::GetFPGATimestamp() + m_period;
167 UpdateAlarm();
168}
169
170void Notifier::Stop() {
Austin Schuh1e69f942020-11-14 15:06:14 -0800171 std::scoped_lock lock(m_processMutex);
172 m_periodic = false;
Brian Silverman8fce7482020-01-05 13:18:21 -0800173 int32_t status = 0;
174 HAL_CancelNotifierAlarm(m_notifier, &status);
Austin Schuh812d0d12021-11-04 20:16:48 -0700175 FRC_CheckErrorStatus(status, "{}", "CancelNotifierAlarm");
Brian Silverman8fce7482020-01-05 13:18:21 -0800176}
177
178void Notifier::UpdateAlarm(uint64_t triggerTime) {
179 int32_t status = 0;
180 // Return if we are being destructed, or were not created successfully
181 auto notifier = m_notifier.load();
Austin Schuh812d0d12021-11-04 20:16:48 -0700182 if (notifier == 0) {
183 return;
184 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800185 HAL_UpdateNotifierAlarm(notifier, triggerTime, &status);
Austin Schuh812d0d12021-11-04 20:16:48 -0700186 FRC_CheckErrorStatus(status, "{}", "UpdateNotifierAlarm");
Brian Silverman8fce7482020-01-05 13:18:21 -0800187}
188
189void Notifier::UpdateAlarm() {
190 UpdateAlarm(static_cast<uint64_t>(m_expirationTime * 1e6));
191}
Austin Schuh812d0d12021-11-04 20:16:48 -0700192
193bool Notifier::SetHALThreadPriority(bool realTime, int32_t priority) {
194 int32_t status = 0;
195 return HAL_SetNotifierThreadPriority(realTime, priority, &status);
196}