@@ -11,180 +11,107 @@ namespace WPILib
11
11
/// </summary>
12
12
public class Notifier : IDisposable
13
13
{
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 ;
18
17
private double m_expirationTime = 0 ;
19
- private double m_period = 0 ;
18
+ private Action m_handler ;
20
19
private bool m_periodic = false ;
20
+ private double m_period = 0 ;
21
+ private volatile bool m_isRunning = false ;
21
22
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 ( )
33
24
{
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 )
41
30
{
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 ( ) ;
67
32
}
33
+ HAL_CleanNotifier ( handle , ref status ) ;
68
34
}
69
35
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 ( )
77
37
{
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 ;
88
40
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 ) ;
91
43
}
92
44
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 )
100
46
{
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 ;
111
48
int status = 0 ;
112
- m_notifier = HAL_InitializeNotifier ( process , IntPtr . Zero , ref status ) ;
49
+ int handle = HAL_InitializeNotifier ( ref status ) ;
113
50
CheckStatusForceThrow ( status ) ;
114
- }
51
+ Interlocked . Exchange ( ref m_notifier , handle ) ;
52
+ m_isRunning = true ;
115
53
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 ( ) ;
124
75
125
- lock ( m_handlerMutex ) { }
76
+ }
77
+ } ) ;
78
+ m_thread . IsBackground = true ;
79
+ m_thread . Start ( ) ;
126
80
}
127
81
128
- /// <summary>
129
- /// Update the HAL alarm time.
130
- /// </summary>
131
- private void UpdateAlarm ( )
82
+ public void SetHandler ( Action handler )
132
83
{
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
+ }
136
88
}
137
89
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>
143
90
public void StartSingle ( double delay )
144
91
{
145
- lock ( m_processMutex )
92
+ lock ( m_processLock )
146
93
{
147
94
m_periodic = false ;
148
95
m_period = delay ;
149
- m_expirationTime = GetFPGATimestamp ( ) + m_period ;
96
+ m_expirationTime = Utility . GetFPGATime ( ) * 1e-6 + delay ;
150
97
UpdateAlarm ( ) ;
151
98
}
152
99
}
153
100
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>
162
101
public void StartPeriodic ( double period )
163
102
{
164
- lock ( m_processMutex )
103
+ lock ( m_processLock )
165
104
{
166
105
m_periodic = true ;
167
106
m_period = period ;
168
- m_expirationTime = GetFPGATimestamp ( ) + m_period ;
107
+ m_expirationTime = Utility . GetFPGATime ( ) * 1e-6 + period ;
169
108
UpdateAlarm ( ) ;
170
109
}
171
110
}
172
111
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>
180
112
public void Stop ( )
181
113
{
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 ) ) ;
187
115
}
188
-
189
116
}
190
117
}
0 commit comments