Files
allwpilib/wpiutil/src/main/native/include/wpi/Synchronization.h
2025-01-06 20:11:34 -08:00

609 lines
18 KiB
C++

// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
#pragma once
#ifdef __cplusplus
#include <climits> // NOLINT
#include <initializer_list>
#include <span>
#endif
/**
* Generic handle for all WPI handle-based interfaces.
*
* Handle data layout:
* - Bits 0-23: Type-specific
* - Bits 24-30: Type
* - Bit 31: Error
*/
typedef unsigned int WPI_Handle; // NOLINT
/** An event handle. */
typedef WPI_Handle WPI_EventHandle; // NOLINT
/** A semaphore handle. */
typedef WPI_Handle WPI_SemaphoreHandle; // NOLINT
#ifdef __cplusplus
namespace wpi {
/** Constant representing an invalid handle. */
constexpr unsigned int kInvalidHandle = 0;
/**
* Standard types for handles.
* @{
*/
constexpr int kHandleTypeEvent = 1;
constexpr int kHandleTypeSemaphore = 2;
constexpr int kHandleTypeCSBase = 3;
constexpr int kHandleTypeNTBase = 16;
constexpr int kHandleTypeHALBase = 48;
constexpr int kHandleTypeUserBase = 80;
/** @} */
/**
* Creates an event. Events have binary state (signaled or not signaled) and
* may be either automatically reset or manually reset. Automatic-reset events
* go to non-signaled state when a WaitForObject is woken up by the event;
* manual-reset events require ResetEvent() to be called to set the event to
* non-signaled state; if ResetEvent() is not called, any waiter on that event
* will immediately wake when called.
*
* @param manualReset true for manual reset, false for automatic reset
* @param initialState true to make the event initially in signaled state
* @return Event handle
*/
WPI_EventHandle CreateEvent(bool manualReset = false,
bool initialState = false);
/**
* Destroys an event. Destruction wakes up any waiters.
*
* @param handle event handle
*/
void DestroyEvent(WPI_EventHandle handle);
/**
* Sets an event to signaled state.
*
* @param handle event handle
*/
void SetEvent(WPI_EventHandle handle);
/**
* Sets an event to non-signaled state.
*
* @param handle event handle
*/
void ResetEvent(WPI_EventHandle handle);
/**
* Creates a semaphore. Semaphores keep an internal counter. Releasing the
* semaphore increases the count. A semaphore with a non-zero count is
* considered signaled. When a waiter wakes up it atomically decrements the
* count by 1. This is generally useful in a single-supplier,
* multiple-consumer scenario.
*
* @param initialCount initial value for the semaphore's internal counter
* @param maximumCount maximum value for the semaphore's internal counter
* @return Semaphore handle
*/
WPI_SemaphoreHandle CreateSemaphore(int initialCount = 0,
int maximumCount = INT_MAX);
/**
* Destroys a semaphore. Destruction wakes up any waiters.
*
* @param handle semaphore handle
*/
void DestroySemaphore(WPI_SemaphoreHandle handle);
/**
* Releases N counts of a semaphore.
*
* @param handle semaphore handle
* @param releaseCount amount to add to semaphore's internal counter;
* must be positive
* @param prevCount if non-null, previous count (output parameter)
* @return True on successful release, false on failure (e.g. release count
* would exceed maximum value, or handle invalid)
*/
bool ReleaseSemaphore(WPI_SemaphoreHandle handle, int releaseCount = 1,
int* prevCount = nullptr);
/**
* Waits for an handle to be signaled.
*
* @param handle handle to wait on
* @return True if handle was signaled, false otherwise (e.g. object was
* destroyed)
*/
bool WaitForObject(WPI_Handle handle);
/**
* Waits for an handle to be signaled, with timeout.
*
* @param handle handle to wait on
* @param timeout timeout in seconds
* @param timedOut if non-null, set to true if timeout reached without handle
* being signaled; set to false otherwise (output)
* @return True if handle was signaled, false otherwise (e.g. object was
* destroyed or timed out)
*/
bool WaitForObject(WPI_Handle handle, double timeout, bool* timedOut);
/**
* Waits for one or more handles to be signaled.
*
* Invalid handles are treated as signaled; the returned array will have the
* handle error bit set for any invalid handles.
*
* @param handles array of handles to wait on
* @param signaled output array for storage of signaled handles; must be at
* least the size of the handles input array
* @return array of signaled handles (points into signaled array)
*/
std::span<WPI_Handle> WaitForObjects(std::span<const WPI_Handle> handles,
std::span<WPI_Handle> signaled);
/**
* Waits for one or more handles to be signaled.
*
* Invalid handles are treated as signaled; the returned array will have the
* handle error bit set for any invalid handles.
*
* @param handles array of handles to wait on
* @param signaled output array for storage of signaled handles; must be at
* least the size of the handles input array
* @return array of signaled handles (points into signaled array)
*/
inline std::span<WPI_Handle> WaitForObjects(
std::initializer_list<WPI_Handle> handles, std::span<WPI_Handle> signaled) {
return WaitForObjects(std::span{handles.begin(), handles.size()}, signaled);
}
/**
* Waits for one or more handles to be signaled, with timeout.
*
* Invalid handles are treated as signaled; the returned array will have the
* handle error bit set for any invalid handles.
*
* @param handles array of handles to wait on
* @param signaled output array for storage of signaled handles; must be at
* least the size of the handles input array
* @param timeout timeout in seconds
* @param timedOut if non-null, set to true if timeout reached without any
* handle being signaled; set to false otherwise (output)
* @return array of signaled handles (points into signaled array)
*/
std::span<WPI_Handle> WaitForObjects(std::span<const WPI_Handle> handles,
std::span<WPI_Handle> signaled,
double timeout, bool* timedOut);
/**
* Waits for one or more handles to be signaled, with timeout.
*
* Invalid handles are treated as signaled; the returned array will have the
* handle error bit set for any invalid handles.
*
* @param handles array of handles to wait on
* @param signaled output array for storage of signaled handles; must be at
* least the size of the handles input array
* @param timeout timeout in seconds
* @param timedOut if non-null, set to true if timeout reached without any
* handle being signaled; set to false otherwise (output)
* @return array of signaled handles (points into signaled array)
*/
inline std::span<WPI_Handle> WaitForObjects(
std::initializer_list<WPI_Handle> handles, std::span<WPI_Handle> signaled,
double timeout, bool* timedOut) {
return WaitForObjects(std::span{handles.begin(), handles.size()}, signaled,
timeout, timedOut);
}
/**
* Sets up signaling for an arbitrary handle. With this function, any handle
* can operate like an event handle.
*
* @param handle Event handle
* @param manualReset true for manual reset, false for automatic reset
* @param initialState true to make the handle initially in signaled state
*/
void CreateSignalObject(WPI_Handle handle, bool manualReset = false,
bool initialState = false);
/**
* Sets a handle to signaled state.
*
* @param handle handle
*/
void SetSignalObject(WPI_Handle handle);
/**
* Sets a handle to non-signaled state.
*
* @param handle handle
*/
void ResetSignalObject(WPI_Handle handle);
/**
* Cleans up signaling for a handle. Destruction wakes up any waiters.
*
* @param handle handle
*/
void DestroySignalObject(WPI_Handle handle);
/**
* An atomic signaling event for synchronization.
*
* Events have binary state (signaled or not signaled) and may be either
* automatically reset or manually reset. Automatic-reset events go to
* non-signaled state when a WaitForObject is woken up by the event;
* manual-reset events require Reset() to be called to set the event to
* non-signaled state; if Reset() is not called, any waiter on that event
* will immediately wake when called.
*/
class Event final {
public:
/**
* Constructor.
*
* @param manualReset true for manual reset, false for automatic reset
* @param initialState true to make the event initially in signaled state
*/
explicit Event(bool manualReset = false, bool initialState = false)
: m_handle{CreateEvent(manualReset, initialState)} {}
~Event() {
if (m_handle != 0) {
DestroyEvent(m_handle);
}
}
Event(const Event&) = delete;
Event& operator=(const Event&) = delete;
Event(Event&& rhs) : m_handle{rhs.m_handle} { rhs.m_handle = 0; }
Event& operator=(Event&& rhs) {
if (m_handle != 0) {
DestroyEvent(m_handle);
}
m_handle = rhs.m_handle;
rhs.m_handle = 0;
return *this;
}
/**
* Gets the event handle (e.g. for WaitForObject).
*
* @return handle
*/
explicit operator WPI_Handle() const { return m_handle; }
/**
* Gets the event handle (e.g. for WaitForObject).
*
* @return handle
*/
WPI_EventHandle GetHandle() const { return m_handle; }
/**
* Sets the event to signaled state.
*/
void Set() { SetEvent(m_handle); }
/**
* Sets the event to non-signaled state.
*/
void Reset() { ResetEvent(m_handle); }
private:
WPI_EventHandle m_handle;
};
/**
* A semaphore for synchronization.
*
* Semaphores keep an internal counter. Releasing the semaphore increases
* the count. A semaphore with a non-zero count is considered signaled.
* When a waiter wakes up it atomically decrements the count by 1. This
* is generally useful in a single-supplier, multiple-consumer scenario.
*/
class Semaphore final {
public:
/**
* Constructor.
*
* @param initialCount initial value for the semaphore's internal counter
* @param maximumCount maximum value for the semaphore's internal counter
*/
explicit Semaphore(int initialCount = 0, int maximumCount = INT_MAX)
: m_handle{CreateSemaphore(initialCount, maximumCount)} {}
~Semaphore() {
if (m_handle != 0) {
DestroySemaphore(m_handle);
}
}
Semaphore(const Semaphore&) = delete;
Semaphore& operator=(const Semaphore&) = delete;
Semaphore(Semaphore&& rhs) : m_handle{rhs.m_handle} { rhs.m_handle = 0; }
Semaphore& operator=(Semaphore&& rhs) {
if (m_handle != 0) {
DestroySemaphore(m_handle);
}
m_handle = rhs.m_handle;
rhs.m_handle = 0;
return *this;
}
/**
* Gets the semaphore handle (e.g. for WaitForObject).
*
* @return handle
*/
explicit operator WPI_Handle() const { return m_handle; }
/**
* Gets the semaphore handle (e.g. for WaitForObject).
*
* @return handle
*/
WPI_SemaphoreHandle GetHandle() const { return m_handle; }
/**
* Releases N counts of the semaphore.
*
* @param releaseCount amount to add to semaphore's internal counter;
* must be positive
* @param prevCount if non-null, previous count (output parameter)
* @return True on successful release, false on failure (e.g. release count
* would exceed maximum value, or handle invalid)
*/
bool Release(int releaseCount = 1, int* prevCount = nullptr) {
return ReleaseSemaphore(m_handle, releaseCount, prevCount);
}
private:
WPI_SemaphoreHandle m_handle;
};
/**
* RAII wrapper for signaling handles.
*
* Sets up signaling for an arbitrary handle. This enables any handle
* to operate like an event handle.
*/
template <typename T>
class SignalObject final {
public:
/**
* Constructor.
*
* @param handle handle
* @param manualReset true for manual reset, false for automatic reset
* @param initialState true to make the handle initially in signaled state
*/
explicit SignalObject(T handle = 0, bool manualReset = false,
bool initialState = false)
: m_handle{handle} {
CreateSignalObject(handle, manualReset, initialState);
}
~SignalObject() {
if (m_handle != 0) {
DestroySignalObject(m_handle);
}
}
SignalObject(const SignalObject&) = delete;
SignalObject& operator=(const SignalObject&) = delete;
SignalObject(SignalObject&& rhs) : m_handle{rhs.m_handle} {
rhs.m_handle = 0;
}
SignalObject& operator=(SignalObject&& rhs) {
if (m_handle != 0) {
DestroySignalObject(m_handle);
}
m_handle = rhs.m_handle;
rhs.m_handle = 0;
return *this;
}
/**
* Gets the handle.
*
* @return handle
*/
/*implicit*/ operator T() const { return m_handle; } // NOLINT
/**
* Gets the handle (e.g. for WaitForObject).
*
* @return handle
*/
T GetHandle() const { return m_handle; }
/**
* Sets the handle to signaled state.
*/
void Set() { SetSignalObject(m_handle); }
/**
* Sets the handle to non-signaled state.
*/
void Reset() { ResetSignalObject(m_handle); }
private:
T m_handle;
};
} // namespace wpi
extern "C" {
#endif // __cplusplus
/**
* Creates an event. Events have binary state (signaled or not signaled) and
* may be either automatically reset or manually reset. Automatic-reset events
* go to non-signaled state when a WaitForObject is woken up by the event;
* manual-reset events require ResetEvent() to be called to set the event to
* non-signaled state; if ResetEvent() is not called, any waiter on that event
* will immediately wake when called.
*
* @param manual_reset true for manual reset, false for automatic reset
* @param initial_state true to make the event initially in signaled state
* @return Event handle
*/
WPI_EventHandle WPI_CreateEvent(int manual_reset, int initial_state);
/**
* Destroys an event. Destruction wakes up any waiters.
*
* @param handle event handle
*/
void WPI_DestroyEvent(WPI_EventHandle handle);
/**
* Sets an event to signaled state.
*
* @param handle event handle
*/
void WPI_SetEvent(WPI_EventHandle handle);
/**
* Sets an event to non-signaled state.
*
* @param handle event handle
*/
void WPI_ResetEvent(WPI_EventHandle handle);
/**
* Creates a semaphore. Semaphores keep an internal counter. Releasing the
* semaphore increases the count. A semaphore with a non-zero count is
* considered signaled. When a waiter wakes up it atomically decrements the
* count by 1. This is generally useful in a single-supplier,
* multiple-consumer scenario.
*
* @param initial_count initial value for the semaphore's internal counter
* @param maximum_count maximum value for the semaphore's internal counter
* @return Semaphore handle
*/
WPI_SemaphoreHandle WPI_CreateSemaphore(int initial_count, int maximum_count);
/**
* Destroys a semaphore. Destruction wakes up any waiters.
*
* @param handle semaphore handle
*/
void WPI_DestroySemaphore(WPI_SemaphoreHandle handle);
/**
* Releases N counts of a semaphore.
*
* @param handle semaphore handle
* @param release_count amount to add to semaphore's internal counter;
* must be positive
* @param prev_count if non-null, previous count (output parameter)
* @return Non-zero on successful release, zero on failure (e.g. release count
* would exceed maximum value, or handle invalid)
*/
int WPI_ReleaseSemaphore(WPI_SemaphoreHandle handle, int release_count,
int* prev_count);
/**
* Waits for an handle to be signaled.
*
* @param handle handle to wait on
* @return Non-zero if handle was signaled, zero otherwise (e.g. object was
* destroyed)
*/
int WPI_WaitForObject(WPI_Handle handle);
/**
* Waits for an handle to be signaled, with timeout.
*
* @param handle handle to wait on
* @param timeout timeout in seconds
* @param timed_out if non-null, set to non-zero if timeout reached without
* handle being signaled; set to zero otherwise (output)
* @return Non-zero if handle was signaled, zero otherwise (e.g. object was
* destroyed or timed out)
*/
int WPI_WaitForObjectTimeout(WPI_Handle handle, double timeout, int* timed_out);
/**
* Waits for one or more handles to be signaled.
*
* Invalid handles are treated as signaled; the returned array will have the
* handle error bit set for any invalid handles.
*
* @param handles array of handles to wait on
* @param handles_count length of the handles array
* @param signaled output array for storage of signaled handles; must be at
* least the size of the handles input array
* @return number of signaled handles
*/
int WPI_WaitForObjects(const WPI_Handle* handles, int handles_count,
WPI_Handle* signaled);
/**
* Waits for one or more handles to be signaled, with timeout.
*
* Invalid handles are treated as signaled; the returned array will have the
* handle error bit set for any invalid handles.
*
* @param handles array of handles to wait on
* @param handles_count length of the handles array
* @param signaled output array for storage of signaled handles; must be at
* least the size of the handles input array
* @param timeout timeout in seconds
* @param timed_out if non-null, set to non-zero if timeout reached without any
* handle being signaled; set to zero otherwise (output)
* @return number of signaled handles
*/
int WPI_WaitForObjectsTimeout(const WPI_Handle* handles, int handles_count,
WPI_Handle* signaled, double timeout,
int* timed_out);
/**
* Sets up signaling for an arbitrary handle. With this function, any handle
* can operate like an event handle.
*
* @param handle handle
* @param manual_reset true for manual reset, false for automatic reset
* @param initial_state true to make the handle initially in signaled state
*/
void WPI_CreateSignalObject(WPI_Handle handle, int manual_reset,
int initial_state);
/**
* Sets a handle to signaled state.
*
* @param handle handle
*/
void WPI_SetSignalObject(WPI_Handle handle);
/**
* Sets a handle to non-signaled state.
*
* @param handle handle
*/
void WPI_ResetSignalObject(WPI_Handle handle);
/**
* Cleans up signaling for a handle. Destruction wakes up any waiters.
*
* @param handle handle
*/
void WPI_DestroySignalObject(WPI_Handle handle);
#ifdef __cplusplus
} // extern "C"
#endif