blob: c457cd12ec724f8d6d935da658ba0462aac467cb [file] [log] [blame]
Brian Silverman41cdd3e2019-01-19 19:48:58 -08001/*----------------------------------------------------------------------------*/
2/* Copyright (c) 2016-2018 FIRST. All Rights Reserved. */
3/* 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 <cstdlib> // For std::atexit()
12#include <memory>
13
14#include <wpi/condition_variable.h>
15#include <wpi/mutex.h>
16
17#include "HALInitializer.h"
18#include "hal/ChipObject.h"
19#include "hal/Errors.h"
20#include "hal/HAL.h"
21#include "hal/handles/UnlimitedHandleResource.h"
22
23using namespace hal;
24
25static constexpr int32_t kTimerInterruptNumber = 28;
26
27static wpi::mutex notifierMutex;
28static std::unique_ptr<tAlarm> notifierAlarm;
29static std::unique_ptr<tInterruptManager> notifierManager;
30static uint64_t closestTrigger{UINT64_MAX};
31
32namespace {
33
34struct Notifier {
35 uint64_t triggerTime = UINT64_MAX;
36 uint64_t triggeredTime = UINT64_MAX;
37 bool active = true;
38 wpi::mutex mutex;
39 wpi::condition_variable cond;
40};
41
42} // namespace
43
44static std::atomic_flag notifierAtexitRegistered{ATOMIC_FLAG_INIT};
45static std::atomic_int notifierRefCount{0};
46
47using namespace hal;
48
49class NotifierHandleContainer
50 : public UnlimitedHandleResource<HAL_NotifierHandle, Notifier,
51 HAL_HandleEnum::Notifier> {
52 public:
53 ~NotifierHandleContainer() {
54 ForEach([](HAL_NotifierHandle handle, Notifier* notifier) {
55 {
56 std::lock_guard<wpi::mutex> lock(notifier->mutex);
57 notifier->triggerTime = UINT64_MAX;
58 notifier->triggeredTime = 0;
59 notifier->active = false;
60 }
61 notifier->cond.notify_all(); // wake up any waiting threads
62 });
63 }
64};
65
66static NotifierHandleContainer* notifierHandles;
67
68static void alarmCallback(uint32_t, void*) {
69 std::lock_guard<wpi::mutex> lock(notifierMutex);
70 int32_t status = 0;
71 uint64_t currentTime = 0;
72
73 // the hardware disables itself after each alarm
74 closestTrigger = UINT64_MAX;
75
76 // process all notifiers
77 notifierHandles->ForEach([&](HAL_NotifierHandle handle, Notifier* notifier) {
78 if (notifier->triggerTime == UINT64_MAX) return;
79 if (currentTime == 0) currentTime = HAL_GetFPGATime(&status);
80 std::unique_lock<wpi::mutex> lock(notifier->mutex);
81 if (notifier->triggerTime < currentTime) {
82 notifier->triggerTime = UINT64_MAX;
83 notifier->triggeredTime = currentTime;
84 lock.unlock();
85 notifier->cond.notify_all();
86 } else if (notifier->triggerTime < closestTrigger) {
87 closestTrigger = notifier->triggerTime;
88 }
89 });
90
91 if (notifierAlarm && closestTrigger != UINT64_MAX) {
92 // Simply truncate the hardware trigger time to 32-bit.
93 notifierAlarm->writeTriggerTime(static_cast<uint32_t>(closestTrigger),
94 &status);
95 // Enable the alarm. The hardware disables itself after each alarm.
96 notifierAlarm->writeEnable(true, &status);
97 }
98}
99
100static void cleanupNotifierAtExit() {
101 notifierAlarm = nullptr;
102 notifierManager = nullptr;
103}
104
105namespace hal {
106namespace init {
107void InitializeNotifier() {
108 static NotifierHandleContainer nH;
109 notifierHandles = &nH;
110}
111} // namespace init
112} // namespace hal
113
114extern "C" {
115
116HAL_NotifierHandle HAL_InitializeNotifier(int32_t* status) {
117 hal::init::CheckInit();
118 if (!notifierAtexitRegistered.test_and_set())
119 std::atexit(cleanupNotifierAtExit);
120
121 if (notifierRefCount.fetch_add(1) == 0) {
122 std::lock_guard<wpi::mutex> lock(notifierMutex);
123 // create manager and alarm if not already created
124 if (!notifierManager) {
125 notifierManager = std::make_unique<tInterruptManager>(
126 1 << kTimerInterruptNumber, false, status);
127 notifierManager->registerHandler(alarmCallback, nullptr, status);
128 notifierManager->enable(status);
129 }
130 if (!notifierAlarm) notifierAlarm.reset(tAlarm::create(status));
131 }
132
133 std::shared_ptr<Notifier> notifier = std::make_shared<Notifier>();
134 HAL_NotifierHandle handle = notifierHandles->Allocate(notifier);
135 if (handle == HAL_kInvalidHandle) {
136 *status = HAL_HANDLE_ERROR;
137 return HAL_kInvalidHandle;
138 }
139 return handle;
140}
141
142void HAL_StopNotifier(HAL_NotifierHandle notifierHandle, int32_t* status) {
143 auto notifier = notifierHandles->Get(notifierHandle);
144 if (!notifier) return;
145
146 {
147 std::lock_guard<wpi::mutex> lock(notifier->mutex);
148 notifier->triggerTime = UINT64_MAX;
149 notifier->triggeredTime = 0;
150 notifier->active = false;
151 }
152 notifier->cond.notify_all(); // wake up any waiting threads
153}
154
155void HAL_CleanNotifier(HAL_NotifierHandle notifierHandle, int32_t* status) {
156 auto notifier = notifierHandles->Free(notifierHandle);
157 if (!notifier) return;
158
159 // Just in case HAL_StopNotifier() wasn't called...
160 {
161 std::lock_guard<wpi::mutex> lock(notifier->mutex);
162 notifier->triggerTime = UINT64_MAX;
163 notifier->triggeredTime = 0;
164 notifier->active = false;
165 }
166 notifier->cond.notify_all();
167
168 if (notifierRefCount.fetch_sub(1) == 1) {
169 // if this was the last notifier, clean up alarm and manager
170 // the notifier can call back into our callback, so don't hold the lock
171 // here (the atomic fetch_sub will prevent multiple parallel entries
172 // into this function)
173
174 // Cleaning up the manager takes up to a second to complete, so don't do
175 // that here. Fix it more permanently in 2019...
176
177 // if (notifierAlarm) notifierAlarm->writeEnable(false, status);
178 // if (notifierManager) notifierManager->disable(status);
179
180 // std::lock_guard<wpi::mutex> lock(notifierMutex);
181 // notifierAlarm = nullptr;
182 // notifierManager = nullptr;
183 // closestTrigger = UINT64_MAX;
184 }
185}
186
187void HAL_UpdateNotifierAlarm(HAL_NotifierHandle notifierHandle,
188 uint64_t triggerTime, int32_t* status) {
189 auto notifier = notifierHandles->Get(notifierHandle);
190 if (!notifier) return;
191
192 {
193 std::lock_guard<wpi::mutex> lock(notifier->mutex);
194 notifier->triggerTime = triggerTime;
195 notifier->triggeredTime = UINT64_MAX;
196 }
197
198 std::lock_guard<wpi::mutex> lock(notifierMutex);
199 // Update alarm time if closer than current.
200 if (triggerTime < closestTrigger) {
201 bool wasActive = (closestTrigger != UINT64_MAX);
202 closestTrigger = triggerTime;
203 // Simply truncate the hardware trigger time to 32-bit.
204 notifierAlarm->writeTriggerTime(static_cast<uint32_t>(closestTrigger),
205 status);
206 // Enable the alarm.
207 if (!wasActive) notifierAlarm->writeEnable(true, status);
208 }
209}
210
211void HAL_CancelNotifierAlarm(HAL_NotifierHandle notifierHandle,
212 int32_t* status) {
213 auto notifier = notifierHandles->Get(notifierHandle);
214 if (!notifier) return;
215
216 {
217 std::lock_guard<wpi::mutex> lock(notifier->mutex);
218 notifier->triggerTime = UINT64_MAX;
219 }
220}
221
222uint64_t HAL_WaitForNotifierAlarm(HAL_NotifierHandle notifierHandle,
223 int32_t* status) {
224 auto notifier = notifierHandles->Get(notifierHandle);
225 if (!notifier) return 0;
226 std::unique_lock<wpi::mutex> lock(notifier->mutex);
227 notifier->cond.wait(lock, [&] {
228 return !notifier->active || notifier->triggeredTime != UINT64_MAX;
229 });
230 return notifier->active ? notifier->triggeredTime : 0;
231}
232
233} // extern "C"