001/*----------------------------------------------------------------------------*/ 002/* Copyright (c) FIRST 2016-2017. All Rights Reserved. */ 003/* Open Source Software - may be modified and shared by FRC teams. The code */ 004/* must be accompanied by the FIRST BSD license file in the root directory of */ 005/* the project. */ 006/*----------------------------------------------------------------------------*/ 007 008package edu.wpi.first.wpilibj; 009 010import java.util.concurrent.atomic.AtomicInteger; 011import java.util.concurrent.locks.ReentrantLock; 012 013import edu.wpi.first.wpilibj.hal.NotifierJNI; 014 015public class Notifier { 016 017 private static class Process implements NotifierJNI.NotifierJNIHandlerFunction { 018 // The lock for the process information. 019 private final ReentrantLock m_processLock = new ReentrantLock(); 020 // The C pointer to the notifier object. We don't use it directly, it is 021 // just passed to the JNI bindings. 022 AtomicInteger m_notifier = new AtomicInteger(); 023 // The time, in microseconds, at which the corresponding handler should be 024 // called. Has the same zero as Utility.getFPGATime(). 025 private double m_expirationTime = 0; 026 // The handler passed in by the user which should be called at the 027 // appropriate interval. 028 private Runnable m_handler; 029 // Whether we are calling the handler just once or periodically. 030 private boolean m_periodic = false; 031 // If periodic, the period of the calling; if just once, stores how long it 032 // is until we call the handler. 033 private double m_period = 0; 034 // Lock on the handler so that the handler is not called before it has 035 // completed. This is only relevant if the handler takes a very long time 036 // to complete (or the period is very short) and when everything is being 037 // destructed. 038 private final ReentrantLock m_handlerLock = new ReentrantLock(); 039 040 public Process(Runnable run) { 041 m_handler = run; 042 m_notifier.set(NotifierJNI.initializeNotifier(this)); 043 } 044 045 @Override 046 @SuppressWarnings("NoFinalizer") 047 protected void finalize() { 048 int handle = m_notifier.getAndSet(0); 049 NotifierJNI.cleanNotifier(handle); 050 m_handlerLock.lock(); 051 } 052 053 /** 054 * Update the alarm hardware to reflect the next alarm. 055 */ 056 private void updateAlarm() { 057 NotifierJNI.updateNotifierAlarm(m_notifier.get(), (long) (m_expirationTime * 1e6)); 058 } 059 060 /** 061 * Handler which is called by the HAL library; it handles the subsequent calling of the user 062 * handler. 063 */ 064 @Override 065 public void apply(long time) { 066 m_processLock.lock(); 067 if (m_periodic) { 068 m_expirationTime += m_period; 069 updateAlarm(); 070 } 071 072 m_handlerLock.lock(); 073 m_processLock.unlock(); 074 075 m_handler.run(); 076 m_handlerLock.unlock(); 077 } 078 079 public void start(double period, boolean periodic) { 080 synchronized (m_processLock) { 081 m_periodic = periodic; 082 m_period = period; 083 m_expirationTime = Utility.getFPGATime() * 1e-6 + period; 084 updateAlarm(); 085 } 086 } 087 088 public void stop() { 089 NotifierJNI.stopNotifierAlarm(m_notifier.get()); 090 091 // Wait for a currently executing handler to complete before returning 092 // from stop() 093 m_handlerLock.lock(); 094 m_handlerLock.unlock(); 095 } 096 } 097 098 private Process m_process; 099 100 /** 101 * Create a Notifier for timer event notification. 102 * 103 * @param run The handler that is called at the notification time which is set using StartSingle 104 * or StartPeriodic. 105 */ 106 public Notifier(Runnable run) { 107 m_process = new Process(run); 108 } 109 110 /** 111 * Register for single event notification. A timer event is queued for a single event after the 112 * specified delay. 113 * 114 * @param delay Seconds to wait before the handler is called. 115 */ 116 public void startSingle(double delay) { 117 m_process.start(delay, false); 118 } 119 120 /** 121 * Register for periodic event notification. A timer event is queued for periodic event 122 * notification. Each time the interrupt occurs, the event will be immediately requeued for the 123 * same time interval. 124 * 125 * @param period Period in seconds to call the handler starting one period after the call to this 126 * method. 127 */ 128 public void startPeriodic(double period) { 129 m_process.start(period, true); 130 } 131 132 /** 133 * Stop timer events from occuring. Stop any repeating timer events from occuring. This will also 134 * remove any single notification events from the queue. If a timer-based call to the registered 135 * handler is in progress, this function will block until the handler call is complete. 136 */ 137 public void stop() { 138 m_process.stop(); 139 } 140}