blob: a6972755427294b603b28c8b039435725c73c063 [file] [log] [blame]
Brian Silverman8fce7482020-01-05 13:18:21 -08001/*----------------------------------------------------------------------------*/
Austin Schuh1e69f942020-11-14 15:06:14 -08002/* Copyright (c) 2016-2020 FIRST. All Rights Reserved. */
Brian Silverman8fce7482020-01-05 13:18:21 -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 "hal/Notifier.h"
9
10#include <atomic>
11#include <chrono>
12#include <cstdio>
13#include <cstring>
14#include <string>
15
Austin Schuh1e69f942020-11-14 15:06:14 -080016#include <wpi/SmallVector.h>
Brian Silverman8fce7482020-01-05 13:18:21 -080017#include <wpi/condition_variable.h>
18#include <wpi/mutex.h>
Brian Silverman8fce7482020-01-05 13:18:21 -080019
20#include "HALInitializer.h"
21#include "NotifierInternal.h"
Austin Schuh1e69f942020-11-14 15:06:14 -080022#include "hal/Errors.h"
23#include "hal/HALBase.h"
Brian Silverman8fce7482020-01-05 13:18:21 -080024#include "hal/cpp/fpga_clock.h"
25#include "hal/handles/UnlimitedHandleResource.h"
Austin Schuh1e69f942020-11-14 15:06:14 -080026#include "hal/simulation/NotifierData.h"
Brian Silverman8fce7482020-01-05 13:18:21 -080027
28namespace {
29struct Notifier {
30 std::string name;
Austin Schuh1e69f942020-11-14 15:06:14 -080031 uint64_t waitTime = UINT64_MAX;
Brian Silverman8fce7482020-01-05 13:18:21 -080032 bool active = true;
Austin Schuh1e69f942020-11-14 15:06:14 -080033 bool waitTimeValid = false; // True if waitTime is set and in the future
34 bool waitingForAlarm = false; // True if in HAL_WaitForNotifierAlarm()
35 uint64_t waitCount = 0; // Counts calls to HAL_WaitForNotifierAlarm()
Brian Silverman8fce7482020-01-05 13:18:21 -080036 wpi::mutex mutex;
37 wpi::condition_variable cond;
38};
39} // namespace
40
41using namespace hal;
42
Austin Schuh1e69f942020-11-14 15:06:14 -080043static wpi::mutex notifiersWaiterMutex;
44static wpi::condition_variable notifiersWaiterCond;
45
Brian Silverman8fce7482020-01-05 13:18:21 -080046class NotifierHandleContainer
47 : public UnlimitedHandleResource<HAL_NotifierHandle, Notifier,
48 HAL_HandleEnum::Notifier> {
49 public:
50 ~NotifierHandleContainer() {
51 ForEach([](HAL_NotifierHandle handle, Notifier* notifier) {
52 {
53 std::scoped_lock lock(notifier->mutex);
54 notifier->active = false;
Austin Schuh1e69f942020-11-14 15:06:14 -080055 notifier->waitTimeValid = false;
Brian Silverman8fce7482020-01-05 13:18:21 -080056 }
57 notifier->cond.notify_all(); // wake up any waiting threads
58 });
Austin Schuh1e69f942020-11-14 15:06:14 -080059 notifiersWaiterCond.notify_all();
Brian Silverman8fce7482020-01-05 13:18:21 -080060 }
61};
62
63static NotifierHandleContainer* notifierHandles;
64static std::atomic<bool> notifiersPaused{false};
65
66namespace hal {
67namespace init {
68void InitializeNotifier() {
69 static NotifierHandleContainer nH;
70 notifierHandles = &nH;
71}
72} // namespace init
73
74void PauseNotifiers() { notifiersPaused = true; }
75
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 }
113 if (count == 0) break;
114 waiters.resize(count);
115 notifiersWaiterCond.wait_for(ulock, std::chrono::duration<double>(1));
116 }
117}
118
119void WakeupWaitNotifiers() {
120 std::unique_lock ulock(notifiersWaiterMutex);
121 int32_t status = 0;
122 uint64_t curTime = HAL_GetFPGATime(&status);
123 wpi::SmallVector<std::pair<HAL_NotifierHandle, uint64_t>, 8> waiters;
124
125 // Wake up Notifiers that have expired timeouts
126 notifierHandles->ForEach([&](HAL_NotifierHandle handle, Notifier* notifier) {
127 std::scoped_lock lock(notifier->mutex);
128
129 // Only wait for the Notifier if it has a valid timeout that's expired
130 if (notifier->active && notifier->waitTimeValid &&
131 curTime >= notifier->waitTime) {
132 waiters.emplace_back(handle, notifier->waitCount);
133 notifier->cond.notify_all();
134 }
135 });
136 for (;;) {
137 int count = 0;
138 int end = waiters.size();
139 while (count < end) {
140 auto& it = waiters[count];
141 if (auto notifier = notifierHandles->Get(it.first)) {
142 std::scoped_lock lock(notifier->mutex);
143
144 // waitCount is used here instead of waitingForAlarm because we want to
145 // wait until HAL_WaitForNotifierAlarm() is exited, then reentered
146 if (notifier->active && notifier->waitCount == it.second) {
147 ++count;
148 continue;
149 }
150 }
151 // No longer need to wait for it, put at end so it can be erased
152 it.swap(waiters[--end]);
153 }
154 if (count == 0) break;
155 waiters.resize(count);
156 notifiersWaiterCond.wait_for(ulock, std::chrono::duration<double>(1));
157 }
158}
Brian Silverman8fce7482020-01-05 13:18:21 -0800159} // namespace hal
160
161extern "C" {
162
163HAL_NotifierHandle HAL_InitializeNotifier(int32_t* status) {
164 hal::init::CheckInit();
165 std::shared_ptr<Notifier> notifier = std::make_shared<Notifier>();
166 HAL_NotifierHandle handle = notifierHandles->Allocate(notifier);
167 if (handle == HAL_kInvalidHandle) {
168 *status = HAL_HANDLE_ERROR;
169 return HAL_kInvalidHandle;
170 }
171 return handle;
172}
173
174void HAL_SetNotifierName(HAL_NotifierHandle notifierHandle, const char* name,
175 int32_t* status) {
176 auto notifier = notifierHandles->Get(notifierHandle);
177 if (!notifier) return;
178 std::scoped_lock lock(notifier->mutex);
179 notifier->name = name;
180}
181
182void HAL_StopNotifier(HAL_NotifierHandle notifierHandle, int32_t* status) {
183 auto notifier = notifierHandles->Get(notifierHandle);
184 if (!notifier) return;
185
186 {
187 std::scoped_lock lock(notifier->mutex);
188 notifier->active = false;
Austin Schuh1e69f942020-11-14 15:06:14 -0800189 notifier->waitTimeValid = false;
Brian Silverman8fce7482020-01-05 13:18:21 -0800190 }
191 notifier->cond.notify_all();
192}
193
194void HAL_CleanNotifier(HAL_NotifierHandle notifierHandle, int32_t* status) {
195 auto notifier = notifierHandles->Free(notifierHandle);
196 if (!notifier) return;
197
198 // Just in case HAL_StopNotifier() wasn't called...
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_UpdateNotifierAlarm(HAL_NotifierHandle notifierHandle,
208 uint64_t triggerTime, int32_t* status) {
209 auto notifier = notifierHandles->Get(notifierHandle);
210 if (!notifier) return;
211
212 {
213 std::scoped_lock lock(notifier->mutex);
214 notifier->waitTime = triggerTime;
Austin Schuh1e69f942020-11-14 15:06:14 -0800215 notifier->waitTimeValid = (triggerTime != UINT64_MAX);
Brian Silverman8fce7482020-01-05 13:18:21 -0800216 }
217
218 // We wake up any waiters to change how long they're sleeping for
219 notifier->cond.notify_all();
220}
221
222void HAL_CancelNotifierAlarm(HAL_NotifierHandle notifierHandle,
223 int32_t* status) {
224 auto notifier = notifierHandles->Get(notifierHandle);
225 if (!notifier) return;
226
227 {
228 std::scoped_lock lock(notifier->mutex);
Austin Schuh1e69f942020-11-14 15:06:14 -0800229 notifier->waitTimeValid = false;
Brian Silverman8fce7482020-01-05 13:18:21 -0800230 }
231}
232
233uint64_t HAL_WaitForNotifierAlarm(HAL_NotifierHandle notifierHandle,
234 int32_t* status) {
235 auto notifier = notifierHandles->Get(notifierHandle);
236 if (!notifier) return 0;
237
Austin Schuh1e69f942020-11-14 15:06:14 -0800238 std::unique_lock ulock(notifiersWaiterMutex);
Brian Silverman8fce7482020-01-05 13:18:21 -0800239 std::unique_lock lock(notifier->mutex);
Austin Schuh1e69f942020-11-14 15:06:14 -0800240 notifier->waitingForAlarm = true;
241 ++notifier->waitCount;
242 ulock.unlock();
243 notifiersWaiterCond.notify_all();
Brian Silverman8fce7482020-01-05 13:18:21 -0800244 while (notifier->active) {
Austin Schuh1e69f942020-11-14 15:06:14 -0800245 uint64_t curTime = HAL_GetFPGATime(status);
246 if (notifier->waitTimeValid && curTime >= notifier->waitTime) {
247 notifier->waitTimeValid = false;
248 notifier->waitingForAlarm = false;
249 return curTime;
Brian Silverman8fce7482020-01-05 13:18:21 -0800250 }
251
Austin Schuh1e69f942020-11-14 15:06:14 -0800252 double waitDuration;
253 if (!notifier->waitTimeValid || notifiersPaused) {
254 // If not running, wait 1000 seconds
255 waitDuration = 1000.0;
256 } else {
257 waitDuration = (notifier->waitTime - curTime) * 1e-6;
258 }
259
260 notifier->cond.wait_for(lock, std::chrono::duration<double>(waitDuration));
Brian Silverman8fce7482020-01-05 13:18:21 -0800261 }
Austin Schuh1e69f942020-11-14 15:06:14 -0800262 notifier->waitingForAlarm = false;
Brian Silverman8fce7482020-01-05 13:18:21 -0800263 return 0;
264}
265
266uint64_t HALSIM_GetNextNotifierTimeout(void) {
267 uint64_t timeout = UINT64_MAX;
268 notifierHandles->ForEach([&](HAL_NotifierHandle, Notifier* notifier) {
269 std::scoped_lock lock(notifier->mutex);
Austin Schuh1e69f942020-11-14 15:06:14 -0800270 if (notifier->active && notifier->waitTimeValid &&
271 timeout > notifier->waitTime)
Brian Silverman8fce7482020-01-05 13:18:21 -0800272 timeout = notifier->waitTime;
273 });
274 return timeout;
275}
276
277int32_t HALSIM_GetNumNotifiers(void) {
278 int32_t count = 0;
279 notifierHandles->ForEach([&](HAL_NotifierHandle, Notifier* notifier) {
280 std::scoped_lock lock(notifier->mutex);
281 if (notifier->active) ++count;
282 });
283 return count;
284}
285
286int32_t HALSIM_GetNotifierInfo(struct HALSIM_NotifierInfo* arr, int32_t size) {
287 int32_t num = 0;
288 notifierHandles->ForEach([&](HAL_NotifierHandle handle, Notifier* notifier) {
289 std::scoped_lock lock(notifier->mutex);
290 if (!notifier->active) return;
291 if (num < size) {
292 arr[num].handle = handle;
293 if (notifier->name.empty()) {
294 std::snprintf(arr[num].name, sizeof(arr[num].name), "Notifier%d",
295 static_cast<int>(getHandleIndex(handle)));
296 } else {
297 std::strncpy(arr[num].name, notifier->name.c_str(),
298 sizeof(arr[num].name));
299 arr[num].name[sizeof(arr[num].name) - 1] = '\0';
300 }
301 arr[num].timeout = notifier->waitTime;
Austin Schuh1e69f942020-11-14 15:06:14 -0800302 arr[num].waitTimeValid = notifier->waitTimeValid;
Brian Silverman8fce7482020-01-05 13:18:21 -0800303 }
304 ++num;
305 });
306 return num;
307}
308
309} // extern "C"