blob: c686eeaa5df8859967030f8395d02e76fca9625c [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>
Brian Silverman8fce7482020-01-05 13:18:21 -080014#include <wpi/condition_variable.h>
15#include <wpi/mutex.h>
Brian Silverman8fce7482020-01-05 13:18:21 -080016
17#include "HALInitializer.h"
18#include "NotifierInternal.h"
Austin Schuh1e69f942020-11-14 15:06:14 -080019#include "hal/Errors.h"
20#include "hal/HALBase.h"
Brian Silverman8fce7482020-01-05 13:18:21 -080021#include "hal/cpp/fpga_clock.h"
22#include "hal/handles/UnlimitedHandleResource.h"
Austin Schuh1e69f942020-11-14 15:06:14 -080023#include "hal/simulation/NotifierData.h"
Brian Silverman8fce7482020-01-05 13:18:21 -080024
25namespace {
26struct Notifier {
27 std::string name;
Austin Schuh1e69f942020-11-14 15:06:14 -080028 uint64_t waitTime = UINT64_MAX;
Brian Silverman8fce7482020-01-05 13:18:21 -080029 bool active = true;
Austin Schuh1e69f942020-11-14 15:06:14 -080030 bool waitTimeValid = false; // True if waitTime is set and in the future
31 bool waitingForAlarm = false; // True if in HAL_WaitForNotifierAlarm()
32 uint64_t waitCount = 0; // Counts calls to HAL_WaitForNotifierAlarm()
Brian Silverman8fce7482020-01-05 13:18:21 -080033 wpi::mutex mutex;
34 wpi::condition_variable cond;
35};
36} // namespace
37
38using namespace hal;
39
Austin Schuh1e69f942020-11-14 15:06:14 -080040static wpi::mutex notifiersWaiterMutex;
41static wpi::condition_variable notifiersWaiterCond;
42
Brian Silverman8fce7482020-01-05 13:18:21 -080043class NotifierHandleContainer
44 : public UnlimitedHandleResource<HAL_NotifierHandle, Notifier,
45 HAL_HandleEnum::Notifier> {
46 public:
47 ~NotifierHandleContainer() {
48 ForEach([](HAL_NotifierHandle handle, Notifier* notifier) {
49 {
50 std::scoped_lock lock(notifier->mutex);
51 notifier->active = false;
Austin Schuh1e69f942020-11-14 15:06:14 -080052 notifier->waitTimeValid = false;
Brian Silverman8fce7482020-01-05 13:18:21 -080053 }
54 notifier->cond.notify_all(); // wake up any waiting threads
55 });
Austin Schuh1e69f942020-11-14 15:06:14 -080056 notifiersWaiterCond.notify_all();
Brian Silverman8fce7482020-01-05 13:18:21 -080057 }
58};
59
60static NotifierHandleContainer* notifierHandles;
61static std::atomic<bool> notifiersPaused{false};
62
63namespace hal {
64namespace init {
65void InitializeNotifier() {
66 static NotifierHandleContainer nH;
67 notifierHandles = &nH;
68}
69} // namespace init
70
Austin Schuh812d0d12021-11-04 20:16:48 -070071void PauseNotifiers() {
72 notifiersPaused = true;
73}
Brian Silverman8fce7482020-01-05 13:18:21 -080074
75void ResumeNotifiers() {
76 notifiersPaused = false;
77 WakeupNotifiers();
78}
79
80void WakeupNotifiers() {
81 notifierHandles->ForEach([](HAL_NotifierHandle handle, Notifier* notifier) {
82 notifier->cond.notify_all();
83 });
84}
Austin Schuh1e69f942020-11-14 15:06:14 -080085
86void WaitNotifiers() {
87 std::unique_lock ulock(notifiersWaiterMutex);
88 wpi::SmallVector<HAL_NotifierHandle, 8> waiters;
89
90 // Wait for all Notifiers to hit HAL_WaitForNotifierAlarm()
91 notifierHandles->ForEach([&](HAL_NotifierHandle handle, Notifier* notifier) {
92 std::scoped_lock lock(notifier->mutex);
93 if (notifier->active && !notifier->waitingForAlarm) {
94 waiters.emplace_back(handle);
95 }
96 });
97 for (;;) {
98 int count = 0;
99 int end = waiters.size();
100 while (count < end) {
101 auto& it = waiters[count];
102 if (auto notifier = notifierHandles->Get(it)) {
103 std::scoped_lock lock(notifier->mutex);
104 if (notifier->active && !notifier->waitingForAlarm) {
105 ++count;
106 continue;
107 }
108 }
109 // No longer need to wait for it, put at end so it can be erased
110 std::swap(it, waiters[--end]);
111 }
Austin Schuh812d0d12021-11-04 20:16:48 -0700112 if (count == 0) {
113 break;
114 }
Austin Schuh1e69f942020-11-14 15:06:14 -0800115 waiters.resize(count);
116 notifiersWaiterCond.wait_for(ulock, std::chrono::duration<double>(1));
117 }
118}
119
120void WakeupWaitNotifiers() {
121 std::unique_lock ulock(notifiersWaiterMutex);
122 int32_t status = 0;
123 uint64_t curTime = HAL_GetFPGATime(&status);
124 wpi::SmallVector<std::pair<HAL_NotifierHandle, uint64_t>, 8> waiters;
125
126 // Wake up Notifiers that have expired timeouts
127 notifierHandles->ForEach([&](HAL_NotifierHandle handle, Notifier* notifier) {
128 std::scoped_lock lock(notifier->mutex);
129
130 // Only wait for the Notifier if it has a valid timeout that's expired
131 if (notifier->active && notifier->waitTimeValid &&
132 curTime >= notifier->waitTime) {
133 waiters.emplace_back(handle, notifier->waitCount);
134 notifier->cond.notify_all();
135 }
136 });
137 for (;;) {
138 int count = 0;
139 int end = waiters.size();
140 while (count < end) {
141 auto& it = waiters[count];
142 if (auto notifier = notifierHandles->Get(it.first)) {
143 std::scoped_lock lock(notifier->mutex);
144
145 // waitCount is used here instead of waitingForAlarm because we want to
146 // wait until HAL_WaitForNotifierAlarm() is exited, then reentered
147 if (notifier->active && notifier->waitCount == it.second) {
148 ++count;
149 continue;
150 }
151 }
152 // No longer need to wait for it, put at end so it can be erased
153 it.swap(waiters[--end]);
154 }
Austin Schuh812d0d12021-11-04 20:16:48 -0700155 if (count == 0) {
156 break;
157 }
Austin Schuh1e69f942020-11-14 15:06:14 -0800158 waiters.resize(count);
159 notifiersWaiterCond.wait_for(ulock, std::chrono::duration<double>(1));
160 }
161}
Brian Silverman8fce7482020-01-05 13:18:21 -0800162} // namespace hal
163
164extern "C" {
165
166HAL_NotifierHandle HAL_InitializeNotifier(int32_t* status) {
167 hal::init::CheckInit();
168 std::shared_ptr<Notifier> notifier = std::make_shared<Notifier>();
169 HAL_NotifierHandle handle = notifierHandles->Allocate(notifier);
170 if (handle == HAL_kInvalidHandle) {
171 *status = HAL_HANDLE_ERROR;
172 return HAL_kInvalidHandle;
173 }
174 return handle;
175}
176
Austin Schuh812d0d12021-11-04 20:16:48 -0700177HAL_Bool HAL_SetNotifierThreadPriority(HAL_Bool realTime, int32_t priority,
178 int32_t* status) {
179 return true;
180}
181
Brian Silverman8fce7482020-01-05 13:18:21 -0800182void HAL_SetNotifierName(HAL_NotifierHandle notifierHandle, const char* name,
183 int32_t* status) {
184 auto notifier = notifierHandles->Get(notifierHandle);
Austin Schuh812d0d12021-11-04 20:16:48 -0700185 if (!notifier) {
186 return;
187 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800188 std::scoped_lock lock(notifier->mutex);
189 notifier->name = name;
190}
191
192void HAL_StopNotifier(HAL_NotifierHandle notifierHandle, int32_t* status) {
193 auto notifier = notifierHandles->Get(notifierHandle);
Austin Schuh812d0d12021-11-04 20:16:48 -0700194 if (!notifier) {
195 return;
196 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800197
198 {
199 std::scoped_lock lock(notifier->mutex);
200 notifier->active = false;
Austin Schuh1e69f942020-11-14 15:06:14 -0800201 notifier->waitTimeValid = false;
Brian Silverman8fce7482020-01-05 13:18:21 -0800202 }
203 notifier->cond.notify_all();
204}
205
206void HAL_CleanNotifier(HAL_NotifierHandle notifierHandle, int32_t* status) {
207 auto notifier = notifierHandles->Free(notifierHandle);
Austin Schuh812d0d12021-11-04 20:16:48 -0700208 if (!notifier) {
209 return;
210 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800211
212 // Just in case HAL_StopNotifier() wasn't called...
213 {
214 std::scoped_lock lock(notifier->mutex);
215 notifier->active = false;
Austin Schuh1e69f942020-11-14 15:06:14 -0800216 notifier->waitTimeValid = false;
Brian Silverman8fce7482020-01-05 13:18:21 -0800217 }
218 notifier->cond.notify_all();
219}
220
221void HAL_UpdateNotifierAlarm(HAL_NotifierHandle notifierHandle,
222 uint64_t triggerTime, int32_t* status) {
223 auto notifier = notifierHandles->Get(notifierHandle);
Austin Schuh812d0d12021-11-04 20:16:48 -0700224 if (!notifier) {
225 return;
226 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800227
228 {
229 std::scoped_lock lock(notifier->mutex);
230 notifier->waitTime = triggerTime;
Austin Schuh1e69f942020-11-14 15:06:14 -0800231 notifier->waitTimeValid = (triggerTime != UINT64_MAX);
Brian Silverman8fce7482020-01-05 13:18:21 -0800232 }
233
234 // We wake up any waiters to change how long they're sleeping for
235 notifier->cond.notify_all();
236}
237
238void HAL_CancelNotifierAlarm(HAL_NotifierHandle notifierHandle,
239 int32_t* status) {
240 auto notifier = notifierHandles->Get(notifierHandle);
Austin Schuh812d0d12021-11-04 20:16:48 -0700241 if (!notifier) {
242 return;
243 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800244
245 {
246 std::scoped_lock lock(notifier->mutex);
Austin Schuh1e69f942020-11-14 15:06:14 -0800247 notifier->waitTimeValid = false;
Brian Silverman8fce7482020-01-05 13:18:21 -0800248 }
249}
250
251uint64_t HAL_WaitForNotifierAlarm(HAL_NotifierHandle notifierHandle,
252 int32_t* status) {
253 auto notifier = notifierHandles->Get(notifierHandle);
Austin Schuh812d0d12021-11-04 20:16:48 -0700254 if (!notifier) {
255 return 0;
256 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800257
Austin Schuh1e69f942020-11-14 15:06:14 -0800258 std::unique_lock ulock(notifiersWaiterMutex);
Brian Silverman8fce7482020-01-05 13:18:21 -0800259 std::unique_lock lock(notifier->mutex);
Austin Schuh1e69f942020-11-14 15:06:14 -0800260 notifier->waitingForAlarm = true;
261 ++notifier->waitCount;
262 ulock.unlock();
263 notifiersWaiterCond.notify_all();
Brian Silverman8fce7482020-01-05 13:18:21 -0800264 while (notifier->active) {
Austin Schuh1e69f942020-11-14 15:06:14 -0800265 uint64_t curTime = HAL_GetFPGATime(status);
266 if (notifier->waitTimeValid && curTime >= notifier->waitTime) {
267 notifier->waitTimeValid = false;
268 notifier->waitingForAlarm = false;
269 return curTime;
Brian Silverman8fce7482020-01-05 13:18:21 -0800270 }
271
Austin Schuh1e69f942020-11-14 15:06:14 -0800272 double waitDuration;
273 if (!notifier->waitTimeValid || notifiersPaused) {
274 // If not running, wait 1000 seconds
275 waitDuration = 1000.0;
276 } else {
277 waitDuration = (notifier->waitTime - curTime) * 1e-6;
278 }
279
280 notifier->cond.wait_for(lock, std::chrono::duration<double>(waitDuration));
Brian Silverman8fce7482020-01-05 13:18:21 -0800281 }
Austin Schuh1e69f942020-11-14 15:06:14 -0800282 notifier->waitingForAlarm = false;
Brian Silverman8fce7482020-01-05 13:18:21 -0800283 return 0;
284}
285
286uint64_t HALSIM_GetNextNotifierTimeout(void) {
287 uint64_t timeout = UINT64_MAX;
288 notifierHandles->ForEach([&](HAL_NotifierHandle, Notifier* notifier) {
289 std::scoped_lock lock(notifier->mutex);
Austin Schuh1e69f942020-11-14 15:06:14 -0800290 if (notifier->active && notifier->waitTimeValid &&
Austin Schuh812d0d12021-11-04 20:16:48 -0700291 timeout > notifier->waitTime) {
Brian Silverman8fce7482020-01-05 13:18:21 -0800292 timeout = notifier->waitTime;
Austin Schuh812d0d12021-11-04 20:16:48 -0700293 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800294 });
295 return timeout;
296}
297
298int32_t HALSIM_GetNumNotifiers(void) {
299 int32_t count = 0;
300 notifierHandles->ForEach([&](HAL_NotifierHandle, Notifier* notifier) {
301 std::scoped_lock lock(notifier->mutex);
Austin Schuh812d0d12021-11-04 20:16:48 -0700302 if (notifier->active) {
303 ++count;
304 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800305 });
306 return count;
307}
308
309int32_t HALSIM_GetNotifierInfo(struct HALSIM_NotifierInfo* arr, int32_t size) {
310 int32_t num = 0;
311 notifierHandles->ForEach([&](HAL_NotifierHandle handle, Notifier* notifier) {
312 std::scoped_lock lock(notifier->mutex);
Austin Schuh812d0d12021-11-04 20:16:48 -0700313 if (!notifier->active) {
314 return;
315 }
Brian Silverman8fce7482020-01-05 13:18:21 -0800316 if (num < size) {
317 arr[num].handle = handle;
318 if (notifier->name.empty()) {
319 std::snprintf(arr[num].name, sizeof(arr[num].name), "Notifier%d",
320 static_cast<int>(getHandleIndex(handle)));
321 } else {
322 std::strncpy(arr[num].name, notifier->name.c_str(),
Austin Schuh75263e32022-02-22 18:05:32 -0800323 sizeof(arr[num].name) - 1);
Brian Silverman8fce7482020-01-05 13:18:21 -0800324 arr[num].name[sizeof(arr[num].name) - 1] = '\0';
325 }
326 arr[num].timeout = notifier->waitTime;
Austin Schuh1e69f942020-11-14 15:06:14 -0800327 arr[num].waitTimeValid = notifier->waitTimeValid;
Brian Silverman8fce7482020-01-05 13:18:21 -0800328 }
329 ++num;
330 });
331 return num;
332}
333
334} // extern "C"