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