blob: 211f7b22e25408f55518ecf8cb9937fcaae20e60 [file] [log] [blame]
Brian Silverman41cdd3e2019-01-19 19:48:58 -08001/*----------------------------------------------------------------------------*/
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -08002/* Copyright (c) 2016-2019 FIRST. All Rights Reserved. */
Brian Silverman41cdd3e2019-01-19 19:48:58 -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
James Kuszmaul4b81d302019-12-14 20:53:14 -080010#include <atomic>
Brian Silverman41cdd3e2019-01-19 19:48:58 -080011#include <chrono>
James Kuszmaul4b81d302019-12-14 20:53:14 -080012#include <cstdio>
13#include <cstring>
14#include <string>
Brian Silverman41cdd3e2019-01-19 19:48:58 -080015
16#include <wpi/condition_variable.h>
17#include <wpi/mutex.h>
18#include <wpi/timestamp.h>
19
20#include "HALInitializer.h"
James Kuszmaul4b81d302019-12-14 20:53:14 -080021#include "NotifierInternal.h"
Brian Silverman41cdd3e2019-01-19 19:48:58 -080022#include "hal/HAL.h"
23#include "hal/cpp/fpga_clock.h"
24#include "hal/handles/UnlimitedHandleResource.h"
James Kuszmaul4b81d302019-12-14 20:53:14 -080025#include "mockdata/NotifierData.h"
Brian Silverman41cdd3e2019-01-19 19:48:58 -080026
27namespace {
28struct Notifier {
James Kuszmaul4b81d302019-12-14 20:53:14 -080029 std::string name;
Brian Silverman41cdd3e2019-01-19 19:48:58 -080030 uint64_t waitTime;
Brian Silverman41cdd3e2019-01-19 19:48:58 -080031 bool active = true;
32 bool running = false;
33 wpi::mutex mutex;
34 wpi::condition_variable cond;
35};
36} // namespace
37
38using namespace hal;
39
40class NotifierHandleContainer
41 : public UnlimitedHandleResource<HAL_NotifierHandle, Notifier,
42 HAL_HandleEnum::Notifier> {
43 public:
44 ~NotifierHandleContainer() {
45 ForEach([](HAL_NotifierHandle handle, Notifier* notifier) {
46 {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -080047 std::scoped_lock lock(notifier->mutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -080048 notifier->active = false;
49 notifier->running = false;
50 }
51 notifier->cond.notify_all(); // wake up any waiting threads
52 });
53 }
54};
55
56static NotifierHandleContainer* notifierHandles;
James Kuszmaul4b81d302019-12-14 20:53:14 -080057static std::atomic<bool> notifiersPaused{false};
Brian Silverman41cdd3e2019-01-19 19:48:58 -080058
59namespace hal {
60namespace init {
61void InitializeNotifier() {
62 static NotifierHandleContainer nH;
63 notifierHandles = &nH;
64}
65} // namespace init
James Kuszmaul4b81d302019-12-14 20:53:14 -080066
67void PauseNotifiers() { notifiersPaused = true; }
68
69void ResumeNotifiers() {
70 notifiersPaused = false;
71 WakeupNotifiers();
72}
73
74void WakeupNotifiers() {
75 notifierHandles->ForEach([](HAL_NotifierHandle handle, Notifier* notifier) {
76 notifier->cond.notify_all();
77 });
78}
Brian Silverman41cdd3e2019-01-19 19:48:58 -080079} // namespace hal
80
81extern "C" {
82
83HAL_NotifierHandle HAL_InitializeNotifier(int32_t* status) {
84 hal::init::CheckInit();
85 std::shared_ptr<Notifier> notifier = std::make_shared<Notifier>();
86 HAL_NotifierHandle handle = notifierHandles->Allocate(notifier);
87 if (handle == HAL_kInvalidHandle) {
88 *status = HAL_HANDLE_ERROR;
89 return HAL_kInvalidHandle;
90 }
91 return handle;
92}
93
James Kuszmaul4b81d302019-12-14 20:53:14 -080094void HAL_SetNotifierName(HAL_NotifierHandle notifierHandle, const char* name,
95 int32_t* status) {
96 auto notifier = notifierHandles->Get(notifierHandle);
97 if (!notifier) return;
98 std::scoped_lock lock(notifier->mutex);
99 notifier->name = name;
100}
101
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800102void HAL_StopNotifier(HAL_NotifierHandle notifierHandle, int32_t* status) {
103 auto notifier = notifierHandles->Get(notifierHandle);
104 if (!notifier) return;
105
106 {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800107 std::scoped_lock lock(notifier->mutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800108 notifier->active = false;
109 notifier->running = false;
110 }
111 notifier->cond.notify_all();
112}
113
114void HAL_CleanNotifier(HAL_NotifierHandle notifierHandle, int32_t* status) {
115 auto notifier = notifierHandles->Free(notifierHandle);
116 if (!notifier) return;
117
118 // Just in case HAL_StopNotifier() wasn't called...
119 {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800120 std::scoped_lock lock(notifier->mutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800121 notifier->active = false;
122 notifier->running = false;
123 }
124 notifier->cond.notify_all();
125}
126
127void HAL_UpdateNotifierAlarm(HAL_NotifierHandle notifierHandle,
128 uint64_t triggerTime, int32_t* status) {
129 auto notifier = notifierHandles->Get(notifierHandle);
130 if (!notifier) return;
131
132 {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800133 std::scoped_lock lock(notifier->mutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800134 notifier->waitTime = triggerTime;
135 notifier->running = true;
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800136 }
137
138 // We wake up any waiters to change how long they're sleeping for
139 notifier->cond.notify_all();
140}
141
142void HAL_CancelNotifierAlarm(HAL_NotifierHandle notifierHandle,
143 int32_t* status) {
144 auto notifier = notifierHandles->Get(notifierHandle);
145 if (!notifier) return;
146
147 {
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800148 std::scoped_lock lock(notifier->mutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800149 notifier->running = false;
150 }
151}
152
153uint64_t HAL_WaitForNotifierAlarm(HAL_NotifierHandle notifierHandle,
154 int32_t* status) {
155 auto notifier = notifierHandles->Get(notifierHandle);
156 if (!notifier) return 0;
157
James Kuszmaul4f3ad3c2019-12-01 16:35:21 -0800158 std::unique_lock lock(notifier->mutex);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800159 while (notifier->active) {
160 double waitTime;
James Kuszmaul4b81d302019-12-14 20:53:14 -0800161 if (!notifier->running || notifiersPaused) {
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800162 waitTime = (HAL_GetFPGATime(status) * 1e-6) + 1000.0;
163 // If not running, wait 1000 seconds
164 } else {
165 waitTime = notifier->waitTime * 1e-6;
166 }
167
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800168 auto timeoutTime =
169 hal::fpga_clock::epoch() + std::chrono::duration<double>(waitTime);
170 notifier->cond.wait_until(lock, timeoutTime);
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800171 if (!notifier->running) continue;
172 if (!notifier->active) break;
James Kuszmaul4b81d302019-12-14 20:53:14 -0800173 uint64_t curTime = HAL_GetFPGATime(status);
174 if (curTime < notifier->waitTime) continue;
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800175 notifier->running = false;
James Kuszmaul4b81d302019-12-14 20:53:14 -0800176 return curTime;
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800177 }
178 return 0;
179}
180
James Kuszmaul4b81d302019-12-14 20:53:14 -0800181uint64_t HALSIM_GetNextNotifierTimeout(void) {
182 uint64_t timeout = UINT64_MAX;
183 notifierHandles->ForEach([&](HAL_NotifierHandle, Notifier* notifier) {
184 std::scoped_lock lock(notifier->mutex);
185 if (notifier->active && notifier->running && timeout > notifier->waitTime)
186 timeout = notifier->waitTime;
187 });
188 return timeout;
189}
190
191int32_t HALSIM_GetNumNotifiers(void) {
192 int32_t count = 0;
193 notifierHandles->ForEach([&](HAL_NotifierHandle, Notifier* notifier) {
194 std::scoped_lock lock(notifier->mutex);
195 if (notifier->active) ++count;
196 });
197 return count;
198}
199
200int32_t HALSIM_GetNotifierInfo(struct HALSIM_NotifierInfo* arr, int32_t size) {
201 int32_t num = 0;
202 notifierHandles->ForEach([&](HAL_NotifierHandle handle, Notifier* notifier) {
203 std::scoped_lock lock(notifier->mutex);
204 if (!notifier->active) return;
205 if (num < size) {
206 arr[num].handle = handle;
207 if (notifier->name.empty()) {
208 std::snprintf(arr[num].name, sizeof(arr[num].name), "Notifier%d",
209 static_cast<int>(getHandleIndex(handle)));
210 } else {
211 std::strncpy(arr[num].name, notifier->name.c_str(),
212 sizeof(arr[num].name));
213 arr[num].name[sizeof(arr[num].name) - 1] = '\0';
214 }
215 arr[num].timeout = notifier->waitTime;
216 arr[num].running = notifier->running;
217 }
218 ++num;
219 });
220 return num;
221}
222
Brian Silverman41cdd3e2019-01-19 19:48:58 -0800223} // extern "C"