blob: d684031208eec6b5a3099e665700eb26ec0cdde1 [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 "hal/Notifier.h"
6
7#include <atomic>
8#include <cstdlib> // For std::atexit()
9#include <memory>
Austin Schuh1e69f942020-11-14 15:06:14 -080010#include <thread>
Brian Silverman8fce7482020-01-05 13:18:21 -080011
Austin Schuh812d0d12021-11-04 20:16:48 -070012#include <fmt/core.h>
Brian Silverman8fce7482020-01-05 13:18:21 -080013#include <wpi/condition_variable.h>
14#include <wpi/mutex.h>
15
16#include "HALInitializer.h"
Austin Schuh1e69f942020-11-14 15:06:14 -080017#include "HALInternal.h"
Brian Silverman8fce7482020-01-05 13:18:21 -080018#include "hal/ChipObject.h"
19#include "hal/Errors.h"
20#include "hal/HAL.h"
Austin Schuh812d0d12021-11-04 20:16:48 -070021#include "hal/Threads.h"
Brian Silverman8fce7482020-01-05 13:18:21 -080022#include "hal/handles/UnlimitedHandleResource.h"
23
24using namespace hal;
25
26static constexpr int32_t kTimerInterruptNumber = 28;
27
28static wpi::mutex notifierMutex;
29static std::unique_ptr<tAlarm> notifierAlarm;
Austin Schuh1e69f942020-11-14 15:06:14 -080030static std::thread notifierThread;
Austin Schuh812d0d12021-11-04 20:16:48 -070031static HAL_Bool notifierThreadRealTime = false;
32static int32_t notifierThreadPriority = 0;
Brian Silverman8fce7482020-01-05 13:18:21 -080033static uint64_t closestTrigger{UINT64_MAX};
34
35namespace {
36
37struct Notifier {
38 uint64_t triggerTime = UINT64_MAX;
39 uint64_t triggeredTime = UINT64_MAX;
40 bool active = true;
41 wpi::mutex mutex;
42 wpi::condition_variable cond;
43};
44
45} // namespace
46
47static std::atomic_flag notifierAtexitRegistered{ATOMIC_FLAG_INIT};
48static std::atomic_int notifierRefCount{0};
Austin Schuh1e69f942020-11-14 15:06:14 -080049static std::atomic_bool notifierRunning{false};
Brian Silverman8fce7482020-01-05 13:18:21 -080050
51using namespace hal;
52
53class NotifierHandleContainer
54 : public UnlimitedHandleResource<HAL_NotifierHandle, Notifier,
55 HAL_HandleEnum::Notifier> {
56 public:
57 ~NotifierHandleContainer() {
58 ForEach([](HAL_NotifierHandle handle, Notifier* notifier) {
59 {
60 std::scoped_lock lock(notifier->mutex);
61 notifier->triggerTime = UINT64_MAX;
62 notifier->triggeredTime = 0;
63 notifier->active = false;
64 }
65 notifier->cond.notify_all(); // wake up any waiting threads
66 });
67 }
68};
69
70static NotifierHandleContainer* notifierHandles;
71
Austin Schuh1e69f942020-11-14 15:06:14 -080072static void alarmCallback() {
Brian Silverman8fce7482020-01-05 13:18:21 -080073 std::scoped_lock lock(notifierMutex);
74 int32_t status = 0;
75 uint64_t currentTime = 0;
76
77 // the hardware disables itself after each alarm
78 closestTrigger = UINT64_MAX;
79
80 // process all notifiers
81 notifierHandles->ForEach([&](HAL_NotifierHandle handle, Notifier* notifier) {
Austin Schuh812d0d12021-11-04 20:16:48 -070082 if (notifier->triggerTime == UINT64_MAX) {
83 return;
84 }
85 if (currentTime == 0) {
86 currentTime = HAL_GetFPGATime(&status);
87 }
Brian Silverman8fce7482020-01-05 13:18:21 -080088 std::unique_lock lock(notifier->mutex);
89 if (notifier->triggerTime < currentTime) {
90 notifier->triggerTime = UINT64_MAX;
91 notifier->triggeredTime = currentTime;
92 lock.unlock();
93 notifier->cond.notify_all();
94 } else if (notifier->triggerTime < closestTrigger) {
95 closestTrigger = notifier->triggerTime;
96 }
97 });
98
99 if (notifierAlarm && closestTrigger != UINT64_MAX) {
100 // Simply truncate the hardware trigger time to 32-bit.
101 notifierAlarm->writeTriggerTime(static_cast<uint32_t>(closestTrigger),
102 &status);
103 // Enable the alarm. The hardware disables itself after each alarm.
104 notifierAlarm->writeEnable(true, &status);
105 }
106}
107
Austin Schuh1e69f942020-11-14 15:06:14 -0800108static void notifierThreadMain() {
109 tRioStatusCode status = 0;
110 tInterruptManager manager{1 << kTimerInterruptNumber, true, &status};
111 while (notifierRunning) {
112 auto triggeredMask = manager.watch(10000, false, &status);
Austin Schuh812d0d12021-11-04 20:16:48 -0700113 if (!notifierRunning) {
114 break;
115 }
116 if (triggeredMask == 0)
117 continue;
Austin Schuh1e69f942020-11-14 15:06:14 -0800118 alarmCallback();
119 }
120}
121
Brian Silverman8fce7482020-01-05 13:18:21 -0800122static void cleanupNotifierAtExit() {
Austin Schuh1e69f942020-11-14 15:06:14 -0800123 int32_t status = 0;
Austin Schuh812d0d12021-11-04 20:16:48 -0700124 if (notifierAlarm)
125 notifierAlarm->writeEnable(false, &status);
Brian Silverman8fce7482020-01-05 13:18:21 -0800126 notifierAlarm = nullptr;
Austin Schuh1e69f942020-11-14 15:06:14 -0800127 notifierRunning = false;
128 hal::ReleaseFPGAInterrupt(kTimerInterruptNumber);
Austin Schuh812d0d12021-11-04 20:16:48 -0700129 if (notifierThread.joinable()) {
130 notifierThread.join();
131 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800132}
133
Austin Schuh812d0d12021-11-04 20:16:48 -0700134namespace hal::init {
Brian Silverman8fce7482020-01-05 13:18:21 -0800135void InitializeNotifier() {
136 static NotifierHandleContainer nH;
137 notifierHandles = &nH;
138}
Austin Schuh812d0d12021-11-04 20:16:48 -0700139} // namespace hal::init
Brian Silverman8fce7482020-01-05 13:18:21 -0800140
141extern "C" {
142
143HAL_NotifierHandle HAL_InitializeNotifier(int32_t* status) {
144 hal::init::CheckInit();
Austin Schuh812d0d12021-11-04 20:16:48 -0700145 if (!notifierAtexitRegistered.test_and_set()) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800146 std::atexit(cleanupNotifierAtExit);
Austin Schuh812d0d12021-11-04 20:16:48 -0700147 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800148
149 if (notifierRefCount.fetch_add(1) == 0) {
150 std::scoped_lock lock(notifierMutex);
Austin Schuh1e69f942020-11-14 15:06:14 -0800151 notifierRunning = true;
152 notifierThread = std::thread(notifierThreadMain);
Austin Schuh812d0d12021-11-04 20:16:48 -0700153
154 auto native = notifierThread.native_handle();
155 HAL_SetThreadPriority(&native, notifierThreadRealTime,
156 notifierThreadPriority, status);
157 if (*status == HAL_THREAD_PRIORITY_ERROR) {
158 *status = 0;
159 fmt::print("{}: HAL Notifier thread\n",
160 HAL_THREAD_PRIORITY_ERROR_MESSAGE);
161 }
162 if (*status == HAL_THREAD_PRIORITY_RANGE_ERROR) {
163 *status = 0;
164 fmt::print("{}: HAL Notifier thread\n",
165 HAL_THREAD_PRIORITY_RANGE_ERROR_MESSAGE);
166 }
167
Austin Schuh1e69f942020-11-14 15:06:14 -0800168 notifierAlarm.reset(tAlarm::create(status));
Brian Silverman8fce7482020-01-05 13:18:21 -0800169 }
170
171 std::shared_ptr<Notifier> notifier = std::make_shared<Notifier>();
172 HAL_NotifierHandle handle = notifierHandles->Allocate(notifier);
173 if (handle == HAL_kInvalidHandle) {
174 *status = HAL_HANDLE_ERROR;
175 return HAL_kInvalidHandle;
176 }
177 return handle;
178}
179
Austin Schuh812d0d12021-11-04 20:16:48 -0700180HAL_Bool HAL_SetNotifierThreadPriority(HAL_Bool realTime, int32_t priority,
181 int32_t* status) {
182 std::scoped_lock lock(notifierMutex);
183 notifierThreadRealTime = realTime;
184 notifierThreadPriority = priority;
185 if (notifierThread.joinable()) {
186 auto native = notifierThread.native_handle();
187 return HAL_SetThreadPriority(&native, realTime, priority, status);
188 } else {
189 return true;
190 }
191}
192
Brian Silverman8fce7482020-01-05 13:18:21 -0800193void HAL_SetNotifierName(HAL_NotifierHandle notifierHandle, const char* name,
194 int32_t* status) {}
195
196void HAL_StopNotifier(HAL_NotifierHandle notifierHandle, int32_t* status) {
197 auto notifier = notifierHandles->Get(notifierHandle);
Austin Schuh812d0d12021-11-04 20:16:48 -0700198 if (!notifier)
199 return;
Brian Silverman8fce7482020-01-05 13:18:21 -0800200
201 {
202 std::scoped_lock lock(notifier->mutex);
203 notifier->triggerTime = UINT64_MAX;
204 notifier->triggeredTime = 0;
205 notifier->active = false;
206 }
207 notifier->cond.notify_all(); // wake up any waiting threads
208}
209
210void HAL_CleanNotifier(HAL_NotifierHandle notifierHandle, int32_t* status) {
211 auto notifier = notifierHandles->Free(notifierHandle);
Austin Schuh812d0d12021-11-04 20:16:48 -0700212 if (!notifier)
213 return;
Brian Silverman8fce7482020-01-05 13:18:21 -0800214
215 // Just in case HAL_StopNotifier() wasn't called...
216 {
217 std::scoped_lock lock(notifier->mutex);
218 notifier->triggerTime = UINT64_MAX;
219 notifier->triggeredTime = 0;
220 notifier->active = false;
221 }
222 notifier->cond.notify_all();
223
224 if (notifierRefCount.fetch_sub(1) == 1) {
Austin Schuh1e69f942020-11-14 15:06:14 -0800225 // if this was the last notifier, clean up alarm and thread
Brian Silverman8fce7482020-01-05 13:18:21 -0800226 // the notifier can call back into our callback, so don't hold the lock
227 // here (the atomic fetch_sub will prevent multiple parallel entries
228 // into this function)
229
Austin Schuh812d0d12021-11-04 20:16:48 -0700230 if (notifierAlarm)
231 notifierAlarm->writeEnable(false, status);
Austin Schuh1e69f942020-11-14 15:06:14 -0800232 notifierRunning = false;
233 hal::ReleaseFPGAInterrupt(kTimerInterruptNumber);
Austin Schuh812d0d12021-11-04 20:16:48 -0700234 if (notifierThread.joinable()) {
235 notifierThread.join();
236 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800237
Austin Schuh1e69f942020-11-14 15:06:14 -0800238 std::scoped_lock lock(notifierMutex);
239 notifierAlarm = nullptr;
240 closestTrigger = UINT64_MAX;
Brian Silverman8fce7482020-01-05 13:18:21 -0800241 }
242}
243
244void HAL_UpdateNotifierAlarm(HAL_NotifierHandle notifierHandle,
245 uint64_t triggerTime, int32_t* status) {
246 auto notifier = notifierHandles->Get(notifierHandle);
Austin Schuh812d0d12021-11-04 20:16:48 -0700247 if (!notifier)
248 return;
Brian Silverman8fce7482020-01-05 13:18:21 -0800249
250 {
251 std::scoped_lock lock(notifier->mutex);
252 notifier->triggerTime = triggerTime;
253 notifier->triggeredTime = UINT64_MAX;
254 }
255
256 std::scoped_lock lock(notifierMutex);
257 // Update alarm time if closer than current.
258 if (triggerTime < closestTrigger) {
259 bool wasActive = (closestTrigger != UINT64_MAX);
260 closestTrigger = triggerTime;
261 // Simply truncate the hardware trigger time to 32-bit.
262 notifierAlarm->writeTriggerTime(static_cast<uint32_t>(closestTrigger),
263 status);
264 // Enable the alarm.
Austin Schuh812d0d12021-11-04 20:16:48 -0700265 if (!wasActive)
266 notifierAlarm->writeEnable(true, status);
Brian Silverman8fce7482020-01-05 13:18:21 -0800267 }
268}
269
270void HAL_CancelNotifierAlarm(HAL_NotifierHandle notifierHandle,
271 int32_t* status) {
272 auto notifier = notifierHandles->Get(notifierHandle);
Austin Schuh812d0d12021-11-04 20:16:48 -0700273 if (!notifier)
274 return;
Brian Silverman8fce7482020-01-05 13:18:21 -0800275
276 {
277 std::scoped_lock lock(notifier->mutex);
278 notifier->triggerTime = UINT64_MAX;
279 }
280}
281
282uint64_t HAL_WaitForNotifierAlarm(HAL_NotifierHandle notifierHandle,
283 int32_t* status) {
284 auto notifier = notifierHandles->Get(notifierHandle);
Austin Schuh812d0d12021-11-04 20:16:48 -0700285 if (!notifier)
286 return 0;
Brian Silverman8fce7482020-01-05 13:18:21 -0800287 std::unique_lock lock(notifier->mutex);
288 notifier->cond.wait(lock, [&] {
289 return !notifier->active || notifier->triggeredTime != UINT64_MAX;
290 });
291 return notifier->active ? notifier->triggeredTime : 0;
292}
293
294} // extern "C"