[wpiutil] Add synchronization primitives

These enable more consistent use of synchronization across the
native libraries.  Users can create Event and Semaphore primitives, but
in addition, libraries can set up any handle as an Event-type signal.
This commit is contained in:
Peter Johnson
2021-08-03 00:05:47 -07:00
parent e32499c546
commit 87e34967ef
7 changed files with 1424 additions and 1 deletions

View File

@@ -63,4 +63,63 @@ public final class WPIUtilJNI {
public static native void addPortForwarder(int port, String remoteHost, int remotePort);
public static native void removePortForwarder(int port);
public static native int createEvent(boolean manualReset, boolean initialState);
public static native void destroyEvent(int eventHandle);
public static native void setEvent(int eventHandle);
public static native void resetEvent(int eventHandle);
public static native int createSemaphore(int initialCount, int maximumCount);
public static native void destroySemaphore(int semHandle);
public static native boolean releaseSemaphore(int semHandle, int releaseCount);
/**
* Waits for an handle to be signaled.
*
* @param handle handle to wait on
* @throws InterruptedException on failure (e.g. object was destroyed)
*/
public static native void waitForObject(int handle) throws InterruptedException;
/**
* Waits for an handle to be signaled, with timeout.
*
* @param handle handle to wait on
* @param timeout timeout in seconds
* @return True if timeout reached without handle being signaled
* @throws InterruptedException on failure (e.g. object was destroyed)
*/
public static native boolean waitForObjectTimeout(int handle, double timeout)
throws InterruptedException;
/**
* Waits for one or more handles to be signaled.
*
* <p>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
* @return array of signaled handles
* @throws InterruptedException on failure (e.g. no objects were signaled)
*/
public static native int[] waitForObjects(int[] handles) throws InterruptedException;
/**
* Waits for one or more handles to be signaled, with timeout.
*
* <p>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 timeout timeout in seconds
* @return array of signaled handles; empty if timeout reached without any handle being signaled
* @throws InterruptedException on failure (e.g. no objects were signaled and no timeout)
*/
public static native int[] waitForObjectsTimeout(int[] handles, double timeout)
throws InterruptedException;
}

View File

@@ -0,0 +1,70 @@
// 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.
package edu.wpi.first.util.concurrent;
import edu.wpi.first.util.WPIUtilJNI;
/**
* An atomic signaling event for synchronization.
*
* <p>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.
*/
public final class Event implements AutoCloseable {
/**
* Constructor.
*
* @param manualReset true for manual reset, false for automatic reset
* @param initialState true to make the event initially in signaled state
*/
public Event(boolean manualReset, boolean initialState) {
m_handle = WPIUtilJNI.createEvent(manualReset, initialState);
}
/**
* Constructor. Initial state is false.
*
* @param manualReset true for manual reset, false for automatic reset
*/
public Event(boolean manualReset) {
this(manualReset, false);
}
/** Constructor. Automatic reset, initial state is false. */
public Event() {
this(false, false);
}
@Override
public void close() {
if (m_handle != 0) {
WPIUtilJNI.destroyEvent(m_handle);
m_handle = 0;
}
}
/**
* Gets the event handle (e.g. for waitForObject).
*
* @return handle
*/
public int getHandle() {
return m_handle;
}
/** Sets the event to signaled state. */
public void set() {
WPIUtilJNI.setEvent(m_handle);
}
/** Sets the event to non-signaled state. */
public void reset() {
WPIUtilJNI.resetEvent(m_handle);
}
private int m_handle;
}

View File

@@ -0,0 +1,80 @@
// 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.
package edu.wpi.first.util.concurrent;
import edu.wpi.first.util.WPIUtilJNI;
/**
* A semaphore for synchronization.
*
* <p>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.
*/
public final class Semaphore implements AutoCloseable {
/**
* Constructor.
*
* @param initialCount initial value for the semaphore's internal counter
* @param maximumCount maximum value for the samephore's internal counter
*/
public Semaphore(int initialCount, int maximumCount) {
m_handle = WPIUtilJNI.createSemaphore(initialCount, maximumCount);
}
/**
* Constructor. Maximum count is Integer.MAX_VALUE.
*
* @param initialCount initial value for the semaphore's internal counter
*/
public Semaphore(int initialCount) {
this(initialCount, Integer.MAX_VALUE);
}
/** Constructor. Initial count is 0, maximum count is Integer.MAX_VALUE. */
public Semaphore() {
this(0, Integer.MAX_VALUE);
}
@Override
public void close() {
if (m_handle != 0) {
WPIUtilJNI.destroySemaphore(m_handle);
m_handle = 0;
}
}
/**
* Gets the semaphore handle (e.g. for waitForObject).
*
* @return handle
*/
public int getHandle() {
return m_handle;
}
/**
* Releases N counts of the semaphore.
*
* @param releaseCount amount to add to semaphore's internal counter; must be positive
* @return True on successful release, false on failure (e.g. release count would exceed maximum
* value, or handle invalid)
*/
public boolean release(int releaseCount) {
return WPIUtilJNI.releaseSemaphore(m_handle, releaseCount);
}
/**
* Releases 1 count of the semaphore.
*
* @return True on successful release, false on failure (e.g. release count would exceed maximum
* value, or handle invalid)
*/
public boolean release() {
return release(1);
}
private int m_handle;
}