Skip to content

Commit

Permalink
Updates to 2018 notifier
Browse files Browse the repository at this point in the history
  • Loading branch information
ThadHouse committed Dec 18, 2017
1 parent 21942e8 commit 41d3483
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 141 deletions.
18 changes: 9 additions & 9 deletions HAL/Delegates/HALNotifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,23 @@ public static void Ping()
{
}

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void HAL_NotifierProcess(ulong time, int handle);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int HAL_InitializeNotifierDelegate(ref int status);
[NativeDelegate] public static HAL_InitializeNotifierDelegate HAL_InitializeNotifier;

[UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int HAL_InitializeNotifierDelegate(HAL_NotifierProcess process, IntPtr param, ref int status);
[NativeDelegate("HAL_InitializeNotifierThreaded")] public static HAL_InitializeNotifierDelegate HAL_InitializeNotifier;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void HAL_StopNotifierDelegate(int notifier_handle, ref int status);
[NativeDelegate] public static HAL_StopNotifierDelegate HAL_StopNotifier;

[UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void HAL_CleanNotifierDelegate(int notifier_handle, ref int status);
[NativeDelegate] public static HAL_CleanNotifierDelegate HAL_CleanNotifier;

[UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate IntPtr HAL_GetNotifierParamDelegate(int notifier_handle, ref int status);
[NativeDelegate] public static HAL_GetNotifierParamDelegate HAL_GetNotifierParam;

[UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void HAL_UpdateNotifierAlarmDelegate(int notifier_handle, ulong triggerTime, ref int status);
[NativeDelegate] public static HAL_UpdateNotifierAlarmDelegate HAL_UpdateNotifierAlarm;

[UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void HAL_StopNotifierAlarmDelegate(int notifier_handle, ref int status);
[NativeDelegate] public static HAL_StopNotifierAlarmDelegate HAL_StopNotifierAlarm;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void HAL_CancelNotifierAlarmDelegate(int notifier_handle, ref int status);
[NativeDelegate] public static HAL_CancelNotifierAlarmDelegate HAL_CancelNotifierAlarm;

[UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate ulong HAL_WaitForNotifierAlarmDelegate(int notifier_handle, ref int status);
[NativeDelegate] public static HAL_WaitForNotifierAlarmDelegate HAL_WaitForNotifierAlarm;
}
}

191 changes: 59 additions & 132 deletions WPILib/Notifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,180 +11,107 @@ namespace WPILib
/// </summary>
public class Notifier : IDisposable
{
private readonly object m_processMutex = new object();
private readonly int m_notifier;
private readonly Action<object> m_handler;
private readonly object m_param;
private Thread m_thread;
private object m_processLock = new object();
private int m_notifier = 0;
private double m_expirationTime = 0;
private double m_period = 0;
private Action m_handler;
private bool m_periodic = false;
private double m_period = 0;
private volatile bool m_isRunning = false;

private readonly object m_handlerMutex = new object();

private readonly HAL_NotifierProcess process;

/// <summary>
/// Notify is called by the HAL Layer. We simply need to pass it through to
/// the user handler.
/// </summary>
/// <param name="currentTimeInt">Current FPGA Time</param>
/// <param name="param">Param passed to the notifier</param>
private void Notify(ulong currentTimeInt, int param)
public void Dispose()
{
// TODO: Use parameter to solve race
bool processMutexEntered = false;
bool handlerMutexEntered = false;
object tempProcessMutex = m_processMutex;
object tempHandlerMutex = m_handlerMutex;

try
int status = 0;
int handle = Interlocked.Exchange(ref m_notifier, 0);
m_isRunning = false;
HAL_StopNotifier(handle, ref status);
if (m_thread != null && m_thread.IsAlive)
{
//Enter the process mutex
Monitor.Enter(tempProcessMutex, ref processMutexEntered);
//Update the alarm if we are a periodic alarm
if (m_periodic)
{
m_expirationTime += m_period;
UpdateAlarm();
}
//Enter the handler mutex before leaving the process mutex
//To ensure safety.
Monitor.Enter(tempHandlerMutex, ref handlerMutexEntered);
Monitor.Exit(tempProcessMutex);
processMutexEntered = false;
m_handler?.Invoke(m_param);
}
finally
{
if (processMutexEntered)
{
Monitor.Exit(tempProcessMutex);
}
if (handlerMutexEntered)
{
Monitor.Exit(tempHandlerMutex);
}
m_thread.Join();
}
HAL_CleanNotifier(handle, ref status);
}


/// <summary>
/// Create a notifier for the timer event notification.
/// </summary>
/// <param name="handler">The callback that is called at the notification time
/// which is set using <see cref="StartSingle"/> or <see cref="StartPeriodic"/></param>
public Notifier(Action handler)
private void UpdateAlarm()
{
if (handler == null)
{
throw new ArgumentNullException(nameof(handler), "Handler must not be null.");
}

m_handler = o => handler();
m_param = null;

process = Notify;

int handle = Interlocked.Add(ref m_notifier, 0);
if (handle == 0) return;
int status = 0;
m_notifier = HAL_InitializeNotifier(process, IntPtr.Zero, ref status);
CheckStatusForceThrow(status);
HAL_UpdateNotifierAlarm(handle, (ulong)(m_expirationTime * 1e6), ref status);
CheckStatus(status);
}

/// <summary>
/// Create a notifier for the timer event notification.
/// </summary>
/// <param name="handler">The callback that is called at the notification time
/// which is set using <see cref="StartSingle"/> or <see cref="StartPeriodic"/></param>
/// <param name="param">The object to pass to the callback.</param>
public Notifier(Action<object> handler, object param)
public Notifier(Action run)
{
if (handler == null)
{
throw new ArgumentNullException(nameof(handler), "Handler must not be null.");
}

m_handler = handler;
m_param = param;

process = Notify;

m_handler = run;
int status = 0;
m_notifier = HAL_InitializeNotifier(process, IntPtr.Zero, ref status);
int handle = HAL_InitializeNotifier(ref status);
CheckStatusForceThrow(status);
}
Interlocked.Exchange(ref m_notifier, handle);
m_isRunning = true;

/// <summary>
/// Disposes of the Notifier
/// </summary>
public void Dispose()
{
int status = 0;
HAL_CleanNotifier(m_notifier, ref status);
CheckStatus(status);
m_thread = new Thread(() =>
{
while (m_isRunning)
{
int sstatus = 0;
int notifier = Interlocked.Add(ref m_notifier, 0);
if (notifier == 0 || !m_isRunning) break;
ulong curTime = HAL_WaitForNotifierAlarm(notifier, ref sstatus);
if (curTime == 0 || !m_isRunning) break;
Action handler = null;
lock (m_processLock)
{
handler = m_handler;
if (m_periodic)
{
m_expirationTime += m_period;
UpdateAlarm();
}
}

handler?.Invoke();

lock (m_handlerMutex) { }
}
});
m_thread.IsBackground = true;
m_thread.Start();
}

/// <summary>
/// Update the HAL alarm time.
/// </summary>
private void UpdateAlarm()
public void SetHandler(Action handler)
{
int status = 0;
HAL_UpdateNotifierAlarm(m_notifier, (ulong)(m_expirationTime * 1e6), ref status);
CheckStatus(status);
lock(m_processLock)
{
m_handler = handler;
}
}

/// <summary>
/// Register for a single event notification
/// </summary>
/// <remarks>A timer event is queued for a single event after the specified delay.</remarks>
/// <param name="delay">Seconds to wait before the handler is called.</param>
public void StartSingle(double delay)
{
lock (m_processMutex)
lock(m_processLock)
{
m_periodic = false;
m_period = delay;
m_expirationTime = GetFPGATimestamp() + m_period;
m_expirationTime = Utility.GetFPGATime() * 1e-6 + delay;
UpdateAlarm();
}
}

/// <summary>
/// Register for periodic event notification..
/// </summary>
/// <remarks> timer event is queued for periodic event notification. Each time the
/// interrupt occurs, the event will be immediately requeued for the same time
/// interval.</remarks>
/// <param name="period">Period in seconds to call the handler starting one
/// period after the call to this method.</param>
public void StartPeriodic(double period)
{
lock (m_processMutex)
lock (m_processLock)
{
m_periodic = true;
m_period = period;
m_expirationTime = GetFPGATimestamp() + m_period;
m_expirationTime = Utility.GetFPGATime() * 1e-6 + period;
UpdateAlarm();
}
}

/// <summary>
/// Stop timer events from occurring.
/// </summary>
/// <remarks>Stop any repeating timer events from occurring. 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.</remarks>
public void Stop()
{
int status = 0;
HAL_StopNotifierAlarm(m_notifier, ref status);
CheckStatus(status);

lock(m_handlerMutex) { }
HAL_CancelNotifierAlarm(Interlocked.Add(ref m_notifier, 0));
}

}
}

0 comments on commit 41d3483

Please sign in to comment.