Skip to content

Commit d3ebe52

Browse files
committed
Replace Timer Factory with Timer.
1 parent fd561f3 commit d3ebe52

22 files changed

+818
-378
lines changed
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
using AsyncFiberWorks.Core;
2+
using AsyncFiberWorks.Fibers;
3+
using System;
4+
using System.Threading;
5+
6+
namespace AsyncFiberWorks.Windows.Timer
7+
{
8+
/// <summary>
9+
/// Timer using WaitableTimerEx in Windows.
10+
/// This timer starts a dedicated thread.
11+
/// </summary>
12+
public class IntervalWaitableTimerEx : IIntervalTimer, IDisposable
13+
{
14+
readonly ThreadFiber _thread;
15+
readonly WaitableTimerEx _waitableTimer;
16+
readonly ManualResetEventSlim _resetEvent = new ManualResetEventSlim();
17+
WaitHandle[] _waitHandles = null;
18+
int _scheduled = 0;
19+
bool _disposed = false;
20+
21+
object _lockObj { get { return _waitableTimer; } }
22+
23+
/// <summary>
24+
/// Create a timer.
25+
/// </summary>
26+
public IntervalWaitableTimerEx()
27+
{
28+
_thread = new ThreadFiber();
29+
_waitableTimer = new WaitableTimerEx(manualReset: false);
30+
}
31+
32+
/// <summary>
33+
/// Start a repeating timer.
34+
/// </summary>
35+
/// <param name="action">The process to be called when the timer expires.</param>
36+
/// <param name="firstIntervalMs">Initial wait time. Must be greater than or equal to 0.</param>
37+
/// <param name="intervalMs">The waiting interval time after the second time. Must be greater than 0.</param>
38+
/// <param name="token">A handle to cancel the timer.</param>
39+
public void ScheduleOnInterval(Action action, int firstIntervalMs, int intervalMs, CancellationToken token = default)
40+
{
41+
if (firstIntervalMs < 0)
42+
{
43+
throw new ArgumentOutOfRangeException(nameof(firstIntervalMs), $"{nameof(firstIntervalMs)} must be greater than or equal to 0.");
44+
}
45+
if (intervalMs <= 0)
46+
{
47+
throw new ArgumentOutOfRangeException(nameof(intervalMs), $"{nameof(intervalMs)} must be greater than 0.");
48+
}
49+
50+
var copiedAction = action;
51+
lock (_lockObj)
52+
{
53+
if (_disposed)
54+
{
55+
throw new ObjectDisposedException(this.GetType().FullName);
56+
}
57+
if (_scheduled > 0)
58+
{
59+
_resetEvent.Set();
60+
}
61+
_scheduled += 1;
62+
63+
_thread.Enqueue(() =>
64+
{
65+
lock (_lockObj)
66+
{
67+
if (_scheduled > 1)
68+
{
69+
_scheduled -= 1;
70+
return;
71+
}
72+
if (_disposed)
73+
{
74+
return;
75+
}
76+
_resetEvent.Reset();
77+
SetWaitHandles(token);
78+
}
79+
80+
_waitableTimer.Set(firstIntervalMs * -10000L, intervalMs);
81+
int index = WaitHandle.WaitAny(_waitHandles);
82+
if (index == 0)
83+
{
84+
copiedAction();
85+
}
86+
else
87+
{
88+
_waitableTimer.Cancel();
89+
}
90+
91+
lock (_lockObj)
92+
{
93+
_scheduled -= 1;
94+
}
95+
});
96+
}
97+
}
98+
99+
private void SetWaitHandles(CancellationToken externalToken)
100+
{
101+
if (externalToken.CanBeCanceled)
102+
{
103+
const int needSize = 3;
104+
if ((_waitHandles?.Length ?? 0) != needSize)
105+
{
106+
_waitHandles = new WaitHandle[needSize];
107+
}
108+
_waitHandles[0] = _waitableTimer;
109+
_waitHandles[1] = _resetEvent.WaitHandle;
110+
_waitHandles[2] = externalToken.WaitHandle;
111+
}
112+
else
113+
{
114+
const int needSize = 2;
115+
if ((_waitHandles?.Length ?? 0) != needSize)
116+
{
117+
_waitHandles = new WaitHandle[needSize];
118+
}
119+
_waitHandles[0] = _waitableTimer;
120+
_waitHandles[1] = _resetEvent.WaitHandle;
121+
}
122+
}
123+
124+
void DisposeResources()
125+
{
126+
_waitableTimer.Dispose();
127+
_resetEvent.Dispose();
128+
_thread.Dispose();
129+
_waitHandles = null;
130+
}
131+
132+
/// <summary>
133+
/// Stop the timer.
134+
/// </summary>
135+
public void Dispose()
136+
{
137+
lock (_lockObj)
138+
{
139+
if (_disposed)
140+
{
141+
return;
142+
}
143+
_disposed = true;
144+
_resetEvent.Set();
145+
_thread.Enqueue(DisposeResources);
146+
}
147+
}
148+
}
149+
}
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
using AsyncFiberWorks.Core;
2+
using AsyncFiberWorks.Fibers;
3+
using System;
4+
using System.Threading;
5+
6+
namespace AsyncFiberWorks.Windows.Timer
7+
{
8+
/// <summary>
9+
/// Timer using WaitableTimerEx in Windows.
10+
/// This timer starts a dedicated thread.
11+
/// </summary>
12+
public class OneshotWaitableTimerEx : IOneshotTimer, IDisposable
13+
{
14+
readonly ThreadFiber _thread;
15+
readonly WaitableTimerEx _waitableTimer;
16+
readonly ManualResetEventSlim _resetEvent = new ManualResetEventSlim();
17+
WaitHandle[] _waitHandles = null;
18+
int _scheduled = 0;
19+
bool _disposed = false;
20+
21+
object _lockObj { get { return _waitableTimer; } }
22+
23+
/// <summary>
24+
/// Create a timer.
25+
/// </summary>
26+
public OneshotWaitableTimerEx()
27+
{
28+
_thread = new ThreadFiber();
29+
_waitableTimer = new WaitableTimerEx(manualReset: false);
30+
}
31+
32+
/// <summary>
33+
/// Start a timer.
34+
/// </summary>
35+
/// <param name="action">The process to be called when the timer expires.</param>
36+
/// <param name="firstIntervalMs">Timer wait time. Must be greater than or equal to 0.</param>
37+
/// <param name="token">A handle to cancel the timer.</param>
38+
public void Schedule(Action action, int firstIntervalMs, CancellationToken token = default)
39+
{
40+
if (firstIntervalMs < 0)
41+
{
42+
throw new ArgumentOutOfRangeException(nameof(firstIntervalMs), $"{nameof(firstIntervalMs)} must be greater than or equal to 0.");
43+
}
44+
45+
var copiedAction = action;
46+
lock (_lockObj)
47+
{
48+
if (_disposed)
49+
{
50+
throw new ObjectDisposedException(this.GetType().FullName);
51+
}
52+
if (_scheduled > 0)
53+
{
54+
_resetEvent.Set();
55+
}
56+
_scheduled += 1;
57+
58+
_thread.Enqueue(() =>
59+
{
60+
lock (_lockObj)
61+
{
62+
if (_scheduled > 1)
63+
{
64+
_scheduled -= 1;
65+
return;
66+
}
67+
if (_disposed)
68+
{
69+
return;
70+
}
71+
_resetEvent.Reset();
72+
SetWaitHandles(token);
73+
}
74+
75+
_waitableTimer.Set(firstIntervalMs * -10000L);
76+
int index = WaitHandle.WaitAny(_waitHandles);
77+
if (index == 0)
78+
{
79+
copiedAction();
80+
}
81+
else
82+
{
83+
_waitableTimer.Cancel();
84+
}
85+
86+
lock (_lockObj)
87+
{
88+
_scheduled -= 1;
89+
}
90+
});
91+
}
92+
}
93+
94+
private void SetWaitHandles(CancellationToken externalToken)
95+
{
96+
if (externalToken.CanBeCanceled)
97+
{
98+
const int needSize = 3;
99+
if ((_waitHandles?.Length ?? 0) != needSize)
100+
{
101+
_waitHandles = new WaitHandle[needSize];
102+
}
103+
_waitHandles[0] = _waitableTimer;
104+
_waitHandles[1] = _resetEvent.WaitHandle;
105+
_waitHandles[2] = externalToken.WaitHandle;
106+
}
107+
else
108+
{
109+
const int needSize = 2;
110+
if ((_waitHandles?.Length ?? 0) != needSize)
111+
{
112+
_waitHandles = new WaitHandle[needSize];
113+
}
114+
_waitHandles[0] = _waitableTimer;
115+
_waitHandles[1] = _resetEvent.WaitHandle;
116+
}
117+
}
118+
119+
void DisposeResources()
120+
{
121+
_waitableTimer.Dispose();
122+
_resetEvent.Dispose();
123+
_thread.Dispose();
124+
_waitHandles = null;
125+
}
126+
127+
/// <summary>
128+
/// Stop the timer.
129+
/// </summary>
130+
public void Dispose()
131+
{
132+
lock (_lockObj)
133+
{
134+
if (_disposed)
135+
{
136+
return;
137+
}
138+
_disposed = true;
139+
_resetEvent.Set();
140+
_thread.Enqueue(DisposeResources);
141+
}
142+
}
143+
}
144+
}

src/AsyncFiberWorks.Windows/Timer/WaitableTimerExFactory.cs

Lines changed: 0 additions & 60 deletions
This file was deleted.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using System;
2+
using System.Threading;
3+
4+
namespace AsyncFiberWorks.Core
5+
{
6+
/// <summary>
7+
/// Repeating timer.
8+
/// </summary>
9+
public interface IIntervalTimer : IDisposable
10+
{
11+
/// <summary>
12+
/// Start a repeating timer.
13+
/// </summary>
14+
/// <param name="action">The process to be called when the timer expires.</param>
15+
/// <param name="firstIntervalMs">Initial wait time. Must be greater than or equal to 0.</param>
16+
/// <param name="intervalMs">The waiting interval time after the second time. Must be greater than 0.</param>
17+
/// <param name="cancellation">A handle to cancel the timer.</param>
18+
void ScheduleOnInterval(Action action, int firstIntervalMs, int intervalMs, CancellationToken cancellation = default);
19+
}
20+
}

src/AsyncFiberWorks/Core/IIntervalTimerFactory.cs

Lines changed: 0 additions & 19 deletions
This file was deleted.

0 commit comments

Comments
 (0)