blob: f9ac75b812aa06a943bc8e0545f77966a6efa068 [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.
4
5#include "wpi/Synchronization.h"
6
7#include <algorithm>
James Kuszmaulcf324122023-01-14 14:07:17 -08008#include <atomic>
Austin Schuh812d0d12021-11-04 20:16:48 -07009#include <cstring>
10#include <mutex>
11
12#include "wpi/DenseMap.h"
13#include "wpi/SmallVector.h"
14#include "wpi/UidVector.h"
15#include "wpi/condition_variable.h"
16#include "wpi/mutex.h"
17
18using namespace wpi;
19
James Kuszmaulcf324122023-01-14 14:07:17 -080020static std::atomic_bool gShutdown{false};
21
Austin Schuh812d0d12021-11-04 20:16:48 -070022namespace {
23
24struct State {
25 int signaled{0};
26 bool autoReset{false};
27 wpi::SmallVector<wpi::condition_variable*, 2> waiters;
28};
29
30struct HandleManager {
James Kuszmaulcf324122023-01-14 14:07:17 -080031 ~HandleManager() { gShutdown = true; }
32
Austin Schuh812d0d12021-11-04 20:16:48 -070033 wpi::mutex mutex;
34 wpi::UidVector<int, 8> eventIds;
35 wpi::UidVector<int, 8> semaphoreIds;
36 wpi::DenseMap<WPI_Handle, State> states;
37};
38
39} // namespace
40
41static HandleManager& GetManager() {
42 static HandleManager manager;
43 return manager;
44}
45
46WPI_EventHandle wpi::CreateEvent(bool manualReset, bool initialState) {
47 auto& manager = GetManager();
James Kuszmaulcf324122023-01-14 14:07:17 -080048 if (gShutdown) {
49 return {};
50 }
Austin Schuh812d0d12021-11-04 20:16:48 -070051 std::scoped_lock lock{manager.mutex};
52
53 auto index = manager.eventIds.emplace_back(0);
54 WPI_EventHandle handle = (kHandleTypeEvent << 24) | (index & 0xffffff);
55
56 // configure state data
57 auto& state = manager.states[handle];
58 state.signaled = initialState ? 1 : 0;
59 state.autoReset = !manualReset;
60
61 return handle;
62}
63
64void wpi::DestroyEvent(WPI_EventHandle handle) {
65 if ((handle >> 24) != kHandleTypeEvent) {
66 return;
67 }
68
69 DestroySignalObject(handle);
70
71 auto& manager = GetManager();
James Kuszmaulcf324122023-01-14 14:07:17 -080072 if (gShutdown) {
73 return;
74 }
Austin Schuh812d0d12021-11-04 20:16:48 -070075 std::scoped_lock lock{manager.mutex};
76 manager.eventIds.erase(handle & 0xffffff);
77}
78
79void wpi::SetEvent(WPI_EventHandle handle) {
80 if ((handle >> 24) != kHandleTypeEvent) {
81 return;
82 }
83
84 SetSignalObject(handle);
85}
86
87void wpi::ResetEvent(WPI_EventHandle handle) {
88 if ((handle >> 24) != kHandleTypeEvent) {
89 return;
90 }
91
92 ResetSignalObject(handle);
93}
94
95WPI_SemaphoreHandle wpi::CreateSemaphore(int initialCount, int maximumCount) {
96 auto& manager = GetManager();
James Kuszmaulcf324122023-01-14 14:07:17 -080097 if (gShutdown) {
98 return {};
99 }
Austin Schuh812d0d12021-11-04 20:16:48 -0700100 std::scoped_lock lock{manager.mutex};
101
102 auto index = manager.semaphoreIds.emplace_back(maximumCount);
103 WPI_EventHandle handle = (kHandleTypeSemaphore << 24) | (index & 0xffffff);
104
105 // configure state data
106 auto& state = manager.states[handle];
107 state.signaled = initialCount;
108 state.autoReset = true;
109
110 return handle;
111}
112
113void wpi::DestroySemaphore(WPI_SemaphoreHandle handle) {
114 if ((handle >> 24) != kHandleTypeSemaphore) {
115 return;
116 }
117
118 DestroySignalObject(handle);
119
120 auto& manager = GetManager();
James Kuszmaulcf324122023-01-14 14:07:17 -0800121 if (gShutdown) {
122 return;
123 }
Austin Schuh812d0d12021-11-04 20:16:48 -0700124 std::scoped_lock lock{manager.mutex};
125 manager.eventIds.erase(handle & 0xffffff);
126}
127
128bool wpi::ReleaseSemaphore(WPI_SemaphoreHandle handle, int releaseCount,
129 int* prevCount) {
130 if ((handle >> 24) != kHandleTypeSemaphore) {
131 return false;
132 }
133 if (releaseCount <= 0) {
134 return false;
135 }
136 int index = handle & 0xffffff;
137
138 auto& manager = GetManager();
James Kuszmaulcf324122023-01-14 14:07:17 -0800139 if (gShutdown) {
140 return true;
141 }
Austin Schuh812d0d12021-11-04 20:16:48 -0700142 std::scoped_lock lock{manager.mutex};
143 auto it = manager.states.find(handle);
144 if (it == manager.states.end()) {
145 return false;
146 }
147 auto& state = it->second;
148 int maxCount = manager.eventIds[index];
149 if (prevCount) {
150 *prevCount = state.signaled;
151 }
152 if ((maxCount - state.signaled) < releaseCount) {
153 return false;
154 }
155 state.signaled += releaseCount;
156 for (auto& waiter : state.waiters) {
157 waiter->notify_all();
158 }
159 return true;
160}
161
162bool wpi::WaitForObject(WPI_Handle handle) {
163 return WaitForObject(handle, -1, nullptr);
164}
165
166bool wpi::WaitForObject(WPI_Handle handle, double timeout, bool* timedOut) {
167 WPI_Handle signaledValue;
168 auto signaled = WaitForObjects(
James Kuszmaulcf324122023-01-14 14:07:17 -0800169 std::span(&handle, 1), std::span(&signaledValue, 1), timeout, timedOut);
Austin Schuh812d0d12021-11-04 20:16:48 -0700170 if (signaled.empty()) {
171 return false;
172 }
173 return (signaled[0] & 0x80000000ul) == 0;
174}
175
James Kuszmaulcf324122023-01-14 14:07:17 -0800176std::span<WPI_Handle> wpi::WaitForObjects(std::span<const WPI_Handle> handles,
177 std::span<WPI_Handle> signaled) {
Austin Schuh812d0d12021-11-04 20:16:48 -0700178 return WaitForObjects(handles, signaled, -1, nullptr);
179}
180
James Kuszmaulcf324122023-01-14 14:07:17 -0800181std::span<WPI_Handle> wpi::WaitForObjects(std::span<const WPI_Handle> handles,
182 std::span<WPI_Handle> signaled,
Austin Schuh812d0d12021-11-04 20:16:48 -0700183 double timeout, bool* timedOut) {
184 auto& manager = GetManager();
James Kuszmaulcf324122023-01-14 14:07:17 -0800185 if (gShutdown) {
186 *timedOut = false;
187 return {};
188 }
Austin Schuh812d0d12021-11-04 20:16:48 -0700189 std::unique_lock lock{manager.mutex};
190 wpi::condition_variable cv;
191 bool addedWaiters = false;
192 bool timedOutVal = false;
193 size_t count = 0;
194
195 for (;;) {
196 for (auto handle : handles) {
197 auto it = manager.states.find(handle);
198 if (it == manager.states.end()) {
199 if (count < signaled.size()) {
200 // treat a non-existent handle as signaled, but set the error bit
201 signaled[count++] = handle | 0x80000000ul;
202 }
203 } else {
204 auto& state = it->second;
205 if (state.signaled > 0) {
206 if (count < signaled.size()) {
207 signaled[count++] = handle;
208 }
209 if (state.autoReset) {
210 --state.signaled;
211 if (state.signaled < 0) {
212 state.signaled = 0;
213 }
214 }
215 }
216 }
217 }
218
219 if (timedOutVal || count != 0) {
220 break;
221 }
222
223 if (timeout == 0) {
224 timedOutVal = true;
225 break;
226 }
227
228 if (!addedWaiters) {
229 addedWaiters = true;
230 for (auto handle : handles) {
231 auto& state = manager.states[handle];
232 state.waiters.emplace_back(&cv);
233 }
234 }
235
236 if (timeout < 0) {
237 cv.wait(lock);
238 } else {
239 auto timeoutTime = std::chrono::steady_clock::now() +
240 std::chrono::duration<double>(timeout);
241 if (cv.wait_until(lock, timeoutTime) == std::cv_status::timeout) {
242 timedOutVal = true;
243 }
244 }
245 }
246
247 if (addedWaiters) {
248 for (auto handle : handles) {
249 auto& state = manager.states[handle];
250 auto it = std::find(state.waiters.begin(), state.waiters.end(), &cv);
251 if (it != state.waiters.end()) {
252 state.waiters.erase(it);
253 }
254 }
255 }
256
257 if (timedOut) {
258 *timedOut = timedOutVal;
259 }
260
261 return signaled.subspan(0, count);
262}
263
264void wpi::CreateSignalObject(WPI_Handle handle, bool manualReset,
265 bool initialState) {
266 auto& manager = GetManager();
James Kuszmaulcf324122023-01-14 14:07:17 -0800267 if (gShutdown) {
268 return;
269 }
Austin Schuh812d0d12021-11-04 20:16:48 -0700270 std::scoped_lock lock{manager.mutex};
271 auto& state = manager.states[handle];
272 state.signaled = initialState ? 1 : 0;
273 state.autoReset = !manualReset;
274}
275
276void wpi::SetSignalObject(WPI_Handle handle) {
277 auto& manager = GetManager();
James Kuszmaulcf324122023-01-14 14:07:17 -0800278 if (gShutdown) {
279 return;
280 }
Austin Schuh812d0d12021-11-04 20:16:48 -0700281 std::scoped_lock lock{manager.mutex};
282 auto it = manager.states.find(handle);
283 if (it == manager.states.end()) {
284 return;
285 }
286 auto& state = it->second;
287 state.signaled = 1;
288 for (auto& waiter : state.waiters) {
289 waiter->notify_all();
290 if (state.autoReset) {
291 // expect the first waiter to reset it
292 break;
293 }
294 }
295}
296
297void wpi::ResetSignalObject(WPI_Handle handle) {
298 auto& manager = GetManager();
James Kuszmaulcf324122023-01-14 14:07:17 -0800299 if (gShutdown) {
300 return;
301 }
Austin Schuh812d0d12021-11-04 20:16:48 -0700302 std::scoped_lock lock{manager.mutex};
303 auto it = manager.states.find(handle);
304 if (it != manager.states.end()) {
305 it->second.signaled = 0;
306 }
307}
308
309void wpi::DestroySignalObject(WPI_Handle handle) {
310 auto& manager = GetManager();
James Kuszmaulcf324122023-01-14 14:07:17 -0800311 if (gShutdown) {
312 return;
313 }
Austin Schuh812d0d12021-11-04 20:16:48 -0700314 std::scoped_lock lock{manager.mutex};
315
316 auto it = manager.states.find(handle);
317 if (it != manager.states.end()) {
318 // wake up any waiters
319 for (auto& waiter : it->second.waiters) {
320 waiter->notify_all();
321 }
322 manager.states.erase(it);
323 }
324}
325
326extern "C" {
327
328WPI_EventHandle WPI_CreateEvent(int manual_reset, int initial_state) {
329 return wpi::CreateEvent(manual_reset != 0, initial_state != 0);
330}
331
332void WPI_DestroyEvent(WPI_EventHandle handle) {
333 wpi::DestroyEvent(handle);
334}
335
336void WPI_SetEvent(WPI_EventHandle handle) {
337 wpi::SetEvent(handle);
338}
339
340void WPI_ResetEvent(WPI_EventHandle handle) {
341 wpi::ResetEvent(handle);
342}
343
344WPI_SemaphoreHandle WPI_CreateSemaphore(int initial_count, int maximum_count) {
345 return wpi::CreateSemaphore(initial_count, maximum_count);
346}
347
348void WPI_DestroySemaphore(WPI_SemaphoreHandle handle) {
349 wpi::DestroySemaphore(handle);
350}
351
352int WPI_ReleaseSemaphore(WPI_SemaphoreHandle handle, int release_count,
353 int* prev_count) {
354 return wpi::ReleaseSemaphore(handle, release_count, prev_count);
355}
356
357int WPI_WaitForObject(WPI_Handle handle) {
358 return wpi::WaitForObject(handle);
359}
360
361int WPI_WaitForObjectTimeout(WPI_Handle handle, double timeout,
362 int* timed_out) {
363 bool timedOutBool;
364 int rv = wpi::WaitForObject(handle, timeout, &timedOutBool);
365 *timed_out = timedOutBool ? 1 : 0;
366 return rv;
367}
368
369int WPI_WaitForObjects(const WPI_Handle* handles, int handles_count,
370 WPI_Handle* signaled) {
James Kuszmaulcf324122023-01-14 14:07:17 -0800371 return wpi::WaitForObjects(std::span(handles, handles_count),
372 std::span(signaled, handles_count))
Austin Schuh812d0d12021-11-04 20:16:48 -0700373 .size();
374}
375
376int WPI_WaitForObjectsTimeout(const WPI_Handle* handles, int handles_count,
377 WPI_Handle* signaled, double timeout,
378 int* timed_out) {
379 bool timedOutBool;
James Kuszmaulcf324122023-01-14 14:07:17 -0800380 auto signaledResult = wpi::WaitForObjects(std::span(handles, handles_count),
381 std::span(signaled, handles_count),
Austin Schuh812d0d12021-11-04 20:16:48 -0700382 timeout, &timedOutBool);
383 *timed_out = timedOutBool ? 1 : 0;
384 return signaledResult.size();
385}
386
387void WPI_CreateSignalObject(WPI_Handle handle, int manual_reset,
388 int initial_state) {
389 wpi::CreateSignalObject(handle, manual_reset, initial_state);
390}
391
392void WPI_SetSignalObject(WPI_Handle handle) {
393 wpi::SetSignalObject(handle);
394}
395
396void WPI_ResetSignalObject(WPI_Handle handle) {
397 wpi::ResetSignalObject(handle);
398}
399
400void WPI_DestroySignalObject(WPI_Handle handle) {
401 wpi::DestroySignalObject(handle);
402}
403
404} // extern "C"