Files
allwpilib/wpilibj/src/athena/java/edu/wpi/first/wpilibj/Notifier.java
Thad House 776a991d61 Moves Notifier over to handles (#97)
Internally, the linked list now uses shared_ptrs instead of raw
pointers. In addition, in the WPILib the notifier handle is now made
atomic. Then before the class is destructed, the handle is now set to 0.
This should help solve one of the existing race conditions. A 0 handle
is correctly handled down at the HAL level.
2016-06-05 07:29:47 -07:00

141 lines
4.7 KiB
Java

/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2016. All Rights Reserved. */
/* Open Source Software - may be modified and shared by FRC teams. The code */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project. */
/*----------------------------------------------------------------------------*/
package edu.wpi.first.wpilibj;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import edu.wpi.first.wpilibj.hal.NotifierJNI;
public class Notifier {
private static class Process implements NotifierJNI.NotifierJNIHandlerFunction {
// The lock for the process information.
private final ReentrantLock m_processLock = new ReentrantLock();
// The C pointer to the notifier object. We don't use it directly, it is
// just passed to the JNI bindings.
AtomicInteger m_notifier = new AtomicInteger();
// The time, in microseconds, at which the corresponding handler should be
// called. Has the same zero as Utility.getFPGATime().
private double m_expirationTime = 0;
// The handler passed in by the user which should be called at the
// appropriate interval.
private Runnable m_handler;
// Whether we are calling the handler just once or periodically.
private boolean m_periodic = false;
// If periodic, the period of the calling; if just once, stores how long it
// is until we call the handler.
private double m_period = 0;
// Lock on the handler so that the handler is not called before it has
// completed. This is only relevant if the handler takes a very long time
// to complete (or the period is very short) and when everything is being
// destructed.
private final ReentrantLock m_handlerLock = new ReentrantLock();
public Process(Runnable run) {
m_handler = run;
m_notifier.set(NotifierJNI.initializeNotifier(this));
}
@Override
@SuppressWarnings("NoFinalizer")
protected void finalize() {
int handle = m_notifier.getAndSet(0);
NotifierJNI.cleanNotifier(handle);
m_handlerLock.lock();
}
/**
* Update the alarm hardware to reflect the next alarm.
*/
private void updateAlarm() {
NotifierJNI.updateNotifierAlarm(m_notifier.get(), (long) (m_expirationTime * 1e6));
}
/**
* Handler which is called by the HAL library; it handles the subsequent calling of the user
* handler.
*/
@Override
public void apply(long time) {
m_processLock.lock();
if (m_periodic) {
m_expirationTime += m_period;
updateAlarm();
}
m_handlerLock.lock();
m_processLock.unlock();
m_handler.run();
m_handlerLock.unlock();
}
public void start(double period, boolean periodic) {
synchronized (m_processLock) {
m_periodic = periodic;
m_period = period;
m_expirationTime = Utility.getFPGATime() * 1e-6 + period;
updateAlarm();
}
}
public void stop() {
NotifierJNI.stopNotifierAlarm(m_notifier.get());
// Wait for a currently executing handler to complete before returning
// from stop()
m_handlerLock.lock();
m_handlerLock.unlock();
}
}
private Process m_process;
/**
* Create a Notifier for timer event notification.
*
* @param run The handler that is called at the notification time which is set using StartSingle
* or StartPeriodic.
*/
public Notifier(Runnable run) {
m_process = new Process(run);
}
/**
* Register for single event notification. A timer event is queued for a single event after the
* specified delay.
*
* @param delay Seconds to wait before the handler is called.
*/
public void startSingle(double delay) {
m_process.start(delay, false);
}
/**
* Register for periodic event notification. A timer event is queued for periodic event
* notification. Each time the interrupt occurs, the event will be immediately requeued for the
* same time interval.
*
* @param period Period in seconds to call the handler starting one period after the call to this
* method.
*/
public void startPeriodic(double period) {
m_process.start(period, true);
}
/**
* Stop timer events from occuring. Stop any repeating timer events from occuring. This will also
* remove any single notification events from the queue. If a timer-based call to the registered
* handler is in progress, this function will block until the handler call is complete.
*/
public void stop() {
m_process.stop();
}
}