blob: 3d2c802cd1604e8dae4587b000ffe822b612800a [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 <chrono>
9#include <cstdio>
10#include <cstring>
11#include <string>
12
Austin Schuh1e69f942020-11-14 15:06:14 -080013#include <wpi/SmallVector.h>
James Kuszmaulb13e13f2023-11-22 20:44:04 -080014#include <wpi/StringExtras.h>
Brian Silverman8fce7482020-01-05 13:18:21 -080015#include <wpi/condition_variable.h>
16#include <wpi/mutex.h>
Brian Silverman8fce7482020-01-05 13:18:21 -080017
18#include "HALInitializer.h"
19#include "NotifierInternal.h"
Austin Schuh1e69f942020-11-14 15:06:14 -080020#include "hal/Errors.h"
21#include "hal/HALBase.h"
Brian Silverman8fce7482020-01-05 13:18:21 -080022#include "hal/cpp/fpga_clock.h"
23#include "hal/handles/UnlimitedHandleResource.h"
Austin Schuh1e69f942020-11-14 15:06:14 -080024#include "hal/simulation/NotifierData.h"
Brian Silverman8fce7482020-01-05 13:18:21 -080025
26namespace {
27struct Notifier {
28 std::string name;
Austin Schuh1e69f942020-11-14 15:06:14 -080029 uint64_t waitTime = UINT64_MAX;
Brian Silverman8fce7482020-01-05 13:18:21 -080030 bool active = true;
Austin Schuh1e69f942020-11-14 15:06:14 -080031 bool waitTimeValid = false; // True if waitTime is set and in the future
32 bool waitingForAlarm = false; // True if in HAL_WaitForNotifierAlarm()
33 uint64_t waitCount = 0; // Counts calls to HAL_WaitForNotifierAlarm()
Brian Silverman8fce7482020-01-05 13:18:21 -080034 wpi::mutex mutex;
35 wpi::condition_variable cond;
36};
37} // namespace
38
39using namespace hal;
40
Austin Schuh1e69f942020-11-14 15:06:14 -080041static wpi::mutex notifiersWaiterMutex;
42static wpi::condition_variable notifiersWaiterCond;
43
Brian Silverman8fce7482020-01-05 13:18:21 -080044class NotifierHandleContainer
45 : public UnlimitedHandleResource<HAL_NotifierHandle, Notifier,
46 HAL_HandleEnum::Notifier> {
47 public:
48 ~NotifierHandleContainer() {
49 ForEach([](HAL_NotifierHandle handle, Notifier* notifier) {
50 {
51 std::scoped_lock lock(notifier->mutex);
52 notifier->active = false;
Austin Schuh1e69f942020-11-14 15:06:14 -080053 notifier->waitTimeValid = false;
Brian Silverman8fce7482020-01-05 13:18:21 -080054 }
55 notifier->cond.notify_all(); // wake up any waiting threads
56 });
Austin Schuh1e69f942020-11-14 15:06:14 -080057 notifiersWaiterCond.notify_all();
Brian Silverman8fce7482020-01-05 13:18:21 -080058 }
59};
60
61static NotifierHandleContainer* notifierHandles;
62static std::atomic<bool> notifiersPaused{false};
63
64namespace hal {
65namespace init {
66void InitializeNotifier() {
67 static NotifierHandleContainer nH;
68 notifierHandles = &nH;
69}
70} // namespace init
71
Austin Schuh812d0d12021-11-04 20:16:48 -070072void PauseNotifiers() {
73 notifiersPaused = true;
74}
Brian Silverman8fce7482020-01-05 13:18:21 -080075
76void ResumeNotifiers() {
77 notifiersPaused = false;
78 WakeupNotifiers();
79}
80
81void WakeupNotifiers() {
82 notifierHandles->ForEach([](HAL_NotifierHandle handle, Notifier* notifier) {
83 notifier->cond.notify_all();
84 });
85}
Austin Schuh1e69f942020-11-14 15:06:14 -080086
87void WaitNotifiers() {
88 std::unique_lock ulock(notifiersWaiterMutex);
89 wpi::SmallVector<HAL_NotifierHandle, 8> waiters;
90
91 // Wait for all Notifiers to hit HAL_WaitForNotifierAlarm()
92 notifierHandles->ForEach([&](HAL_NotifierHandle handle, Notifier* notifier) {
93 std::scoped_lock lock(notifier->mutex);
94 if (notifier->active && !notifier->waitingForAlarm) {
95 waiters.emplace_back(handle);
96 }
97 });
98 for (;;) {
99 int count = 0;
100 int end = waiters.size();
101 while (count < end) {
102 auto& it = waiters[count];
103 if (auto notifier = notifierHandles->Get(it)) {
104 std::scoped_lock lock(notifier->mutex);
105 if (notifier->active && !notifier->waitingForAlarm) {
106 ++count;
107 continue;
108 }
109 }
110 // No longer need to wait for it, put at end so it can be erased
111 std::swap(it, waiters[--end]);
112 }
Austin Schuh812d0d12021-11-04 20:16:48 -0700113 if (count == 0) {
114 break;
115 }
Austin Schuh1e69f942020-11-14 15:06:14 -0800116 waiters.resize(count);
117 notifiersWaiterCond.wait_for(ulock, std::chrono::duration<double>(1));
118 }
119}
120
121void WakeupWaitNotifiers() {
122 std::unique_lock ulock(notifiersWaiterMutex);
123 int32_t status = 0;
124 uint64_t curTime = HAL_GetFPGATime(&status);
125 wpi::SmallVector<std::pair<HAL_NotifierHandle, uint64_t>, 8> waiters;
126
127 // Wake up Notifiers that have expired timeouts
128 notifierHandles->ForEach([&](HAL_NotifierHandle handle, Notifier* notifier) {
129 std::scoped_lock lock(notifier->mutex);
130
131 // Only wait for the Notifier if it has a valid timeout that's expired
132 if (notifier->active && notifier->waitTimeValid &&
133 curTime >= notifier->waitTime) {
134 waiters.emplace_back(handle, notifier->waitCount);
135 notifier->cond.notify_all();
136 }
137 });
138 for (;;) {
139 int count = 0;
140 int end = waiters.size();
141 while (count < end) {
142 auto& it = waiters[count];
143 if (auto notifier = notifierHandles->Get(it.first)) {
144 std::scoped_lock lock(notifier->mutex);
145
146 // waitCount is used here instead of waitingForAlarm because we want to
147 // wait until HAL_WaitForNotifierAlarm() is exited, then reentered
148 if (notifier->active && notifier->waitCount == it.second) {
149 ++count;
150 continue;
151 }
152 }
153 // No longer need to wait for it, put at end so it can be erased
154 it.swap(waiters[--end]);
155 }
Austin Schuh812d0d12021-11-04 20:16:48 -0700156 if (count == 0) {
157 break;
158 }
Austin Schuh1e69f942020-11-14 15:06:14 -0800159 waiters.resize(count);
160 notifiersWaiterCond.wait_for(ulock, std::chrono::duration<double>(1));
161 }
162}
Brian Silverman8fce7482020-01-05 13:18:21 -0800163} // namespace hal
164
165extern "C" {
166
167HAL_NotifierHandle HAL_InitializeNotifier(int32_t* status) {
168 hal::init::CheckInit();
169 std::shared_ptr<Notifier> notifier = std::make_shared<Notifier>();
170 HAL_NotifierHandle handle = notifierHandles->Allocate(notifier);
171 if (handle == HAL_kInvalidHandle) {
172 *status = HAL_HANDLE_ERROR;
173 return HAL_kInvalidHandle;
174 }
175 return handle;
176}
177
Austin Schuh812d0d12021-11-04 20:16:48 -0700178HAL_Bool HAL_SetNotifierThreadPriority(HAL_Bool realTime, int32_t priority,
179 int32_t* status) {
180 return true;
181}
182
Brian Silverman8fce7482020-01-05 13:18:21 -0800183void HAL_SetNotifierName(HAL_NotifierHandle notifierHandle, const char* name,
184 int32_t* status) {
185 auto notifier = notifierHandles->Get(notifierHandle);
Austin Schuh812d0d12021-11-04 20:16:48 -0700186 if (!notifier) {
187 return;
188 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800189 std::scoped_lock lock(notifier->mutex);
190 notifier->name = name;
191}
192
193void HAL_StopNotifier(HAL_NotifierHandle notifierHandle, int32_t* status) {
194 auto notifier = notifierHandles->Get(notifierHandle);
Austin Schuh812d0d12021-11-04 20:16:48 -0700195 if (!notifier) {
196 return;
197 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800198
199 {
200 std::scoped_lock lock(notifier->mutex);
201 notifier->active = false;
Austin Schuh1e69f942020-11-14 15:06:14 -0800202 notifier->waitTimeValid = false;
Brian Silverman8fce7482020-01-05 13:18:21 -0800203 }
204 notifier->cond.notify_all();
205}
206
207void HAL_CleanNotifier(HAL_NotifierHandle notifierHandle, int32_t* status) {
208 auto notifier = notifierHandles->Free(notifierHandle);
Austin Schuh812d0d12021-11-04 20:16:48 -0700209 if (!notifier) {
210 return;
211 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800212
213 // Just in case HAL_StopNotifier() wasn't called...
214 {
215 std::scoped_lock lock(notifier->mutex);
216 notifier->active = false;
Austin Schuh1e69f942020-11-14 15:06:14 -0800217 notifier->waitTimeValid = false;
Brian Silverman8fce7482020-01-05 13:18:21 -0800218 }
219 notifier->cond.notify_all();
220}
221
222void HAL_UpdateNotifierAlarm(HAL_NotifierHandle notifierHandle,
223 uint64_t triggerTime, int32_t* status) {
224 auto notifier = notifierHandles->Get(notifierHandle);
Austin Schuh812d0d12021-11-04 20:16:48 -0700225 if (!notifier) {
226 return;
227 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800228
229 {
230 std::scoped_lock lock(notifier->mutex);
231 notifier->waitTime = triggerTime;
Austin Schuh1e69f942020-11-14 15:06:14 -0800232 notifier->waitTimeValid = (triggerTime != UINT64_MAX);
Brian Silverman8fce7482020-01-05 13:18:21 -0800233 }
234
235 // We wake up any waiters to change how long they're sleeping for
236 notifier->cond.notify_all();
237}
238
239void HAL_CancelNotifierAlarm(HAL_NotifierHandle notifierHandle,
240 int32_t* status) {
241 auto notifier = notifierHandles->Get(notifierHandle);
Austin Schuh812d0d12021-11-04 20:16:48 -0700242 if (!notifier) {
243 return;
244 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800245
246 {
247 std::scoped_lock lock(notifier->mutex);
Austin Schuh1e69f942020-11-14 15:06:14 -0800248 notifier->waitTimeValid = false;
Brian Silverman8fce7482020-01-05 13:18:21 -0800249 }
250}
251
252uint64_t HAL_WaitForNotifierAlarm(HAL_NotifierHandle notifierHandle,
253 int32_t* status) {
254 auto notifier = notifierHandles->Get(notifierHandle);
Austin Schuh812d0d12021-11-04 20:16:48 -0700255 if (!notifier) {
256 return 0;
257 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800258
Austin Schuh1e69f942020-11-14 15:06:14 -0800259 std::unique_lock ulock(notifiersWaiterMutex);
Brian Silverman8fce7482020-01-05 13:18:21 -0800260 std::unique_lock lock(notifier->mutex);
Austin Schuh1e69f942020-11-14 15:06:14 -0800261 notifier->waitingForAlarm = true;
262 ++notifier->waitCount;
263 ulock.unlock();
264 notifiersWaiterCond.notify_all();
Brian Silverman8fce7482020-01-05 13:18:21 -0800265 while (notifier->active) {
Austin Schuh1e69f942020-11-14 15:06:14 -0800266 uint64_t curTime = HAL_GetFPGATime(status);
267 if (notifier->waitTimeValid && curTime >= notifier->waitTime) {
268 notifier->waitTimeValid = false;
269 notifier->waitingForAlarm = false;
270 return curTime;
Brian Silverman8fce7482020-01-05 13:18:21 -0800271 }
272
Austin Schuh1e69f942020-11-14 15:06:14 -0800273 double waitDuration;
274 if (!notifier->waitTimeValid || notifiersPaused) {
275 // If not running, wait 1000 seconds
276 waitDuration = 1000.0;
277 } else {
278 waitDuration = (notifier->waitTime - curTime) * 1e-6;
279 }
280
281 notifier->cond.wait_for(lock, std::chrono::duration<double>(waitDuration));
Brian Silverman8fce7482020-01-05 13:18:21 -0800282 }
Austin Schuh1e69f942020-11-14 15:06:14 -0800283 notifier->waitingForAlarm = false;
Brian Silverman8fce7482020-01-05 13:18:21 -0800284 return 0;
285}
286
287uint64_t HALSIM_GetNextNotifierTimeout(void) {
288 uint64_t timeout = UINT64_MAX;
289 notifierHandles->ForEach([&](HAL_NotifierHandle, Notifier* notifier) {
290 std::scoped_lock lock(notifier->mutex);
Austin Schuh1e69f942020-11-14 15:06:14 -0800291 if (notifier->active && notifier->waitTimeValid &&
Austin Schuh812d0d12021-11-04 20:16:48 -0700292 timeout > notifier->waitTime) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800293 timeout = notifier->waitTime;
Austin Schuh812d0d12021-11-04 20:16:48 -0700294 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800295 });
296 return timeout;
297}
298
299int32_t HALSIM_GetNumNotifiers(void) {
300 int32_t count = 0;
301 notifierHandles->ForEach([&](HAL_NotifierHandle, Notifier* notifier) {
302 std::scoped_lock lock(notifier->mutex);
Austin Schuh812d0d12021-11-04 20:16:48 -0700303 if (notifier->active) {
304 ++count;
305 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800306 });
307 return count;
308}
309
310int32_t HALSIM_GetNotifierInfo(struct HALSIM_NotifierInfo* arr, int32_t size) {
311 int32_t num = 0;
312 notifierHandles->ForEach([&](HAL_NotifierHandle handle, Notifier* notifier) {
313 std::scoped_lock lock(notifier->mutex);
Austin Schuh812d0d12021-11-04 20:16:48 -0700314 if (!notifier->active) {
315 return;
316 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800317 if (num < size) {
318 arr[num].handle = handle;
319 if (notifier->name.empty()) {
James Kuszmaulb13e13f2023-11-22 20:44:04 -0800320 wpi::format_to_n_c_str(arr[num].name, sizeof(arr[num].name),
321 "Notifier{}",
322 static_cast<int>(getHandleIndex(handle)));
Brian Silverman8fce7482020-01-05 13:18:21 -0800323 } else {
324 std::strncpy(arr[num].name, notifier->name.c_str(),
Austin Schuh75263e32022-02-22 18:05:32 -0800325 sizeof(arr[num].name) - 1);
Brian Silverman8fce7482020-01-05 13:18:21 -0800326 arr[num].name[sizeof(arr[num].name) - 1] = '\0';
327 }
328 arr[num].timeout = notifier->waitTime;
Austin Schuh1e69f942020-11-14 15:06:14 -0800329 arr[num].waitTimeValid = notifier->waitTimeValid;
Brian Silverman8fce7482020-01-05 13:18:21 -0800330 }
331 ++num;
332 });
333 return num;
334}
335
336} // extern "C"