Skip to content

Commit 41d3483

Browse files
committed
Updates to 2018 notifier
1 parent 21942e8 commit 41d3483

File tree

2 files changed

+68
-141
lines changed

2 files changed

+68
-141
lines changed

HAL/Delegates/HALNotifier.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,23 +19,23 @@ public static void Ping()
1919
{
2020
}
2121

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

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

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

31-
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate IntPtr HAL_GetNotifierParamDelegate(int notifier_handle, ref int status);
32-
[NativeDelegate] public static HAL_GetNotifierParamDelegate HAL_GetNotifierParam;
33-
3431
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void HAL_UpdateNotifierAlarmDelegate(int notifier_handle, ulong triggerTime, ref int status);
3532
[NativeDelegate] public static HAL_UpdateNotifierAlarmDelegate HAL_UpdateNotifierAlarm;
3633

37-
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void HAL_StopNotifierAlarmDelegate(int notifier_handle, ref int status);
38-
[NativeDelegate] public static HAL_StopNotifierAlarmDelegate HAL_StopNotifierAlarm;
34+
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void HAL_CancelNotifierAlarmDelegate(int notifier_handle, ref int status);
35+
[NativeDelegate] public static HAL_CancelNotifierAlarmDelegate HAL_CancelNotifierAlarm;
36+
37+
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate ulong HAL_WaitForNotifierAlarmDelegate(int notifier_handle, ref int status);
38+
[NativeDelegate] public static HAL_WaitForNotifierAlarmDelegate HAL_WaitForNotifierAlarm;
3939
}
4040
}
4141

WPILib/Notifier.cs

Lines changed: 59 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -11,180 +11,107 @@ namespace WPILib
1111
/// </summary>
1212
public class Notifier : IDisposable
1313
{
14-
private readonly object m_processMutex = new object();
15-
private readonly int m_notifier;
16-
private readonly Action<object> m_handler;
17-
private readonly object m_param;
14+
private Thread m_thread;
15+
private object m_processLock = new object();
16+
private int m_notifier = 0;
1817
private double m_expirationTime = 0;
19-
private double m_period = 0;
18+
private Action m_handler;
2019
private bool m_periodic = false;
20+
private double m_period = 0;
21+
private volatile bool m_isRunning = false;
2122

22-
private readonly object m_handlerMutex = new object();
23-
24-
private readonly HAL_NotifierProcess process;
25-
26-
/// <summary>
27-
/// Notify is called by the HAL Layer. We simply need to pass it through to
28-
/// the user handler.
29-
/// </summary>
30-
/// <param name="currentTimeInt">Current FPGA Time</param>
31-
/// <param name="param">Param passed to the notifier</param>
32-
private void Notify(ulong currentTimeInt, int param)
23+
public void Dispose()
3324
{
34-
// TODO: Use parameter to solve race
35-
bool processMutexEntered = false;
36-
bool handlerMutexEntered = false;
37-
object tempProcessMutex = m_processMutex;
38-
object tempHandlerMutex = m_handlerMutex;
39-
40-
try
25+
int status = 0;
26+
int handle = Interlocked.Exchange(ref m_notifier, 0);
27+
m_isRunning = false;
28+
HAL_StopNotifier(handle, ref status);
29+
if (m_thread != null && m_thread.IsAlive)
4130
{
42-
//Enter the process mutex
43-
Monitor.Enter(tempProcessMutex, ref processMutexEntered);
44-
//Update the alarm if we are a periodic alarm
45-
if (m_periodic)
46-
{
47-
m_expirationTime += m_period;
48-
UpdateAlarm();
49-
}
50-
//Enter the handler mutex before leaving the process mutex
51-
//To ensure safety.
52-
Monitor.Enter(tempHandlerMutex, ref handlerMutexEntered);
53-
Monitor.Exit(tempProcessMutex);
54-
processMutexEntered = false;
55-
m_handler?.Invoke(m_param);
56-
}
57-
finally
58-
{
59-
if (processMutexEntered)
60-
{
61-
Monitor.Exit(tempProcessMutex);
62-
}
63-
if (handlerMutexEntered)
64-
{
65-
Monitor.Exit(tempHandlerMutex);
66-
}
31+
m_thread.Join();
6732
}
33+
HAL_CleanNotifier(handle, ref status);
6834
}
6935

70-
71-
/// <summary>
72-
/// Create a notifier for the timer event notification.
73-
/// </summary>
74-
/// <param name="handler">The callback that is called at the notification time
75-
/// which is set using <see cref="StartSingle"/> or <see cref="StartPeriodic"/></param>
76-
public Notifier(Action handler)
36+
private void UpdateAlarm()
7737
{
78-
if (handler == null)
79-
{
80-
throw new ArgumentNullException(nameof(handler), "Handler must not be null.");
81-
}
82-
83-
m_handler = o => handler();
84-
m_param = null;
85-
86-
process = Notify;
87-
38+
int handle = Interlocked.Add(ref m_notifier, 0);
39+
if (handle == 0) return;
8840
int status = 0;
89-
m_notifier = HAL_InitializeNotifier(process, IntPtr.Zero, ref status);
90-
CheckStatusForceThrow(status);
41+
HAL_UpdateNotifierAlarm(handle, (ulong)(m_expirationTime * 1e6), ref status);
42+
CheckStatus(status);
9143
}
9244

93-
/// <summary>
94-
/// Create a notifier for the timer event notification.
95-
/// </summary>
96-
/// <param name="handler">The callback that is called at the notification time
97-
/// which is set using <see cref="StartSingle"/> or <see cref="StartPeriodic"/></param>
98-
/// <param name="param">The object to pass to the callback.</param>
99-
public Notifier(Action<object> handler, object param)
45+
public Notifier(Action run)
10046
{
101-
if (handler == null)
102-
{
103-
throw new ArgumentNullException(nameof(handler), "Handler must not be null.");
104-
}
105-
106-
m_handler = handler;
107-
m_param = param;
108-
109-
process = Notify;
110-
47+
m_handler = run;
11148
int status = 0;
112-
m_notifier = HAL_InitializeNotifier(process, IntPtr.Zero, ref status);
49+
int handle = HAL_InitializeNotifier(ref status);
11350
CheckStatusForceThrow(status);
114-
}
51+
Interlocked.Exchange(ref m_notifier, handle);
52+
m_isRunning = true;
11553

116-
/// <summary>
117-
/// Disposes of the Notifier
118-
/// </summary>
119-
public void Dispose()
120-
{
121-
int status = 0;
122-
HAL_CleanNotifier(m_notifier, ref status);
123-
CheckStatus(status);
54+
m_thread = new Thread(() =>
55+
{
56+
while (m_isRunning)
57+
{
58+
int sstatus = 0;
59+
int notifier = Interlocked.Add(ref m_notifier, 0);
60+
if (notifier == 0 || !m_isRunning) break;
61+
ulong curTime = HAL_WaitForNotifierAlarm(notifier, ref sstatus);
62+
if (curTime == 0 || !m_isRunning) break;
63+
Action handler = null;
64+
lock (m_processLock)
65+
{
66+
handler = m_handler;
67+
if (m_periodic)
68+
{
69+
m_expirationTime += m_period;
70+
UpdateAlarm();
71+
}
72+
}
73+
74+
handler?.Invoke();
12475

125-
lock (m_handlerMutex) { }
76+
}
77+
});
78+
m_thread.IsBackground = true;
79+
m_thread.Start();
12680
}
12781

128-
/// <summary>
129-
/// Update the HAL alarm time.
130-
/// </summary>
131-
private void UpdateAlarm()
82+
public void SetHandler(Action handler)
13283
{
133-
int status = 0;
134-
HAL_UpdateNotifierAlarm(m_notifier, (ulong)(m_expirationTime * 1e6), ref status);
135-
CheckStatus(status);
84+
lock(m_processLock)
85+
{
86+
m_handler = handler;
87+
}
13688
}
13789

138-
/// <summary>
139-
/// Register for a single event notification
140-
/// </summary>
141-
/// <remarks>A timer event is queued for a single event after the specified delay.</remarks>
142-
/// <param name="delay">Seconds to wait before the handler is called.</param>
14390
public void StartSingle(double delay)
14491
{
145-
lock (m_processMutex)
92+
lock(m_processLock)
14693
{
14794
m_periodic = false;
14895
m_period = delay;
149-
m_expirationTime = GetFPGATimestamp() + m_period;
96+
m_expirationTime = Utility.GetFPGATime() * 1e-6 + delay;
15097
UpdateAlarm();
15198
}
15299
}
153100

154-
/// <summary>
155-
/// Register for periodic event notification..
156-
/// </summary>
157-
/// <remarks> timer event is queued for periodic event notification. Each time the
158-
/// interrupt occurs, the event will be immediately requeued for the same time
159-
/// interval.</remarks>
160-
/// <param name="period">Period in seconds to call the handler starting one
161-
/// period after the call to this method.</param>
162101
public void StartPeriodic(double period)
163102
{
164-
lock (m_processMutex)
103+
lock (m_processLock)
165104
{
166105
m_periodic = true;
167106
m_period = period;
168-
m_expirationTime = GetFPGATimestamp() + m_period;
107+
m_expirationTime = Utility.GetFPGATime() * 1e-6 + period;
169108
UpdateAlarm();
170109
}
171110
}
172111

173-
/// <summary>
174-
/// Stop timer events from occurring.
175-
/// </summary>
176-
/// <remarks>Stop any repeating timer events from occurring. This will also remove any
177-
/// single notification events from the queue.
178-
/// If a timer-based call to the registered handler is in progress, this
179-
/// function will block until the handler call is complete.</remarks>
180112
public void Stop()
181113
{
182-
int status = 0;
183-
HAL_StopNotifierAlarm(m_notifier, ref status);
184-
CheckStatus(status);
185-
186-
lock(m_handlerMutex) { }
114+
HAL_CancelNotifierAlarm(Interlocked.Add(ref m_notifier, 0));
187115
}
188-
189116
}
190117
}

0 commit comments

Comments
 (0)