Skip to content

Commit 9846fc6

Browse files
committed
Added MainQueue to assist with scenarios where you want to execute the work on a specific thread (the main thread for example)
1 parent b6a860b commit 9846fc6

File tree

2 files changed

+150
-21
lines changed

2 files changed

+150
-21
lines changed

DispatchQueue/MainQueue.cs

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
using System;
2+
using System.Threading;
3+
4+
#nullable enable
5+
6+
namespace Dispatch
7+
{
8+
/// <summary>
9+
/// SerialQueue which takes work to be executed serially.
10+
/// Work is executed when calling TryDoWork
11+
/// </summary>
12+
public class MainQueue : IDispatchQueue
13+
{
14+
#region Internal Declarations
15+
16+
/// <summary>
17+
/// Class used to intercept the Dispatch calls from the internal Serial Queue
18+
/// </summary>
19+
private class WorkHolder : IDispatcher
20+
{
21+
WaitCallback? mCurrentWork = null;
22+
object? mCurrentContext;
23+
24+
public void QueueWorkItem(WaitCallback work, object? context)
25+
{
26+
if (mCurrentWork != null)
27+
{
28+
throw new InvalidOperationException("Queueing work but previous work is not complete");
29+
}
30+
31+
mCurrentWork = work;
32+
mCurrentContext = context;
33+
}
34+
35+
public bool TryDoWork()
36+
{
37+
if (mCurrentWork == null)
38+
{
39+
return false;
40+
}
41+
42+
// make a copy because executing the work will likely cause QueueWorkItem to be called
43+
WaitCallback workCopy = mCurrentWork;
44+
mCurrentWork = null;
45+
46+
workCopy.Invoke(mCurrentContext);
47+
48+
return true;
49+
}
50+
}
51+
52+
#endregion
53+
54+
55+
#region Member Variables
56+
57+
/// <summary>
58+
/// SerialQueue which will hold all our work to do
59+
/// </summary>
60+
private readonly SerialQueue mQueue;
61+
62+
63+
/// <summary>
64+
/// WorkHolder that will intercept the Dispatch calls from mQueue
65+
/// </summary>
66+
private readonly WorkHolder mWorkHolder = new WorkHolder();
67+
68+
#endregion
69+
70+
71+
#region Constructors
72+
73+
/// <summary>
74+
/// Default constructor for MainQueue
75+
/// </summary>
76+
public MainQueue()
77+
{
78+
mQueue = new SerialQueue(mWorkHolder);
79+
}
80+
81+
#endregion
82+
83+
84+
#region Public Methods
85+
86+
/// <summary>
87+
/// Will dispatch the work delegate to this queue once at least the specified amount of time has passed
88+
/// </summary>
89+
/// <param name="when">Amount of time to wait before submitting the work to this queue</param>
90+
/// <param name="context">User data to pass to the work delegate</param>
91+
/// <param name="work">Delegate which will perform the work. Must not be null</param>
92+
public void DispatchAfter(TimeSpan when, object? context, WaitCallback work)
93+
{
94+
mQueue.DispatchAfter(when, context, work);
95+
}
96+
97+
98+
/// <summary>
99+
/// Will dispatch the work delegate to this queue after the current time has passed the specified date
100+
/// </summary>
101+
/// <param name="when">Date after which the work will be submitted to this queue</param>
102+
/// <param name="context">User data to pass to the work delegate</param>
103+
/// <param name="work">Delegate which will perform the work. Must not be null</param>
104+
public void DispatchAfter(DateTime when, object? context, WaitCallback work)
105+
{
106+
mQueue.DispatchAfter(when, context, work);
107+
}
108+
109+
110+
/// <summary>
111+
/// Will dispatch the work delegate to the thread pool once all the previously dispatched work is completed
112+
/// </summary>
113+
/// <param name="context">User data to pass to the work delegate</param>
114+
/// <param name="work">Delegate which will perform the work. Must not be null</param>
115+
public void DispatchAsync(object? context, WaitCallback work)
116+
{
117+
mQueue.DispatchAsync(context, work);
118+
}
119+
120+
121+
/// <summary>
122+
/// Will attempt to dequeue and execute work which has been queued
123+
/// </summary>
124+
/// <returns>true if work has been dequeued and done, otherwise false</returns>
125+
public bool TryDoWork()
126+
{
127+
return mWorkHolder.TryDoWork();
128+
}
129+
130+
#endregion
131+
}
132+
}

DispatchQueue/SerialQueue.cs

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,7 @@
99
namespace Dispatch
1010
{
1111
/// <summary>
12-
/// <para>
1312
/// SerialQueue which takes work to be executed serially on a given thread pool
14-
/// </para>
15-
/// This implementation attempts to avoid allocations wherever possible
1613
/// </summary>
1714
public class SerialQueue : IDispatchQueue
1815
{
@@ -92,45 +89,45 @@ public SerialQueue(IDispatcher dispatcher)
9289
#region Public Methods
9390

9491
/// <summary>
95-
/// Will dispatch the work delegate to the thread pool once all the previously dispatched work is completed
92+
/// Will dispatch the work delegate to this queue once at least the specified amount of time has passed
9693
/// </summary>
94+
/// <param name="when">Amount of time to wait before submitting the work to this queue</param>
9795
/// <param name="context">User data to pass to the work delegate</param>
9896
/// <param name="work">Delegate which will perform the work. Must not be null</param>
99-
public void DispatchAsync(object? context, WaitCallback work)
97+
public void DispatchAfter(TimeSpan when, object? context, WaitCallback work)
10098
{
101-
if (work == null)
102-
{
103-
throw new ArgumentNullException("work");
104-
}
105-
106-
mQueue.Enqueue(new WorkData { Work = work, Context = context });
107-
108-
// try to dequeue and run a task if there isn't one running already.
109-
AttemptDequeue();
99+
mTimerQueue.DispatchAfter(when, context, work);
110100
}
111101

112102

113103
/// <summary>
114-
/// Will dispatch the work delegate to this queue once at least the specified amount of time has passed
104+
/// Will dispatch the work delegate to this queue after the current time has passed the specified date
115105
/// </summary>
116-
/// <param name="when">Amount of time to wait before submitting the work to this queue</param>
106+
/// <param name="when">Date after which the work will be submitted to this queue</param>
117107
/// <param name="context">User data to pass to the work delegate</param>
118108
/// <param name="work">Delegate which will perform the work. Must not be null</param>
119-
public void DispatchAfter(TimeSpan when, object? context, WaitCallback work)
109+
public void DispatchAfter(DateTime when, object? context, WaitCallback work)
120110
{
121111
mTimerQueue.DispatchAfter(when, context, work);
122112
}
123113

124114

125115
/// <summary>
126-
/// Will dispatch the work delegate to this queue after the current time has passed the specified date
116+
/// Will dispatch the work delegate to the thread pool once all the previously dispatched work is completed
127117
/// </summary>
128-
/// <param name="when">Date after which the work will be submitted to this queue</param>
129118
/// <param name="context">User data to pass to the work delegate</param>
130119
/// <param name="work">Delegate which will perform the work. Must not be null</param>
131-
public void DispatchAfter(DateTime when, object? context, WaitCallback work)
120+
public void DispatchAsync(object? context, WaitCallback work)
132121
{
133-
mTimerQueue.DispatchAfter(when, context, work);
122+
if (work == null)
123+
{
124+
throw new ArgumentNullException("work");
125+
}
126+
127+
mQueue.Enqueue(new WorkData { Work = work, Context = context });
128+
129+
// try to dequeue and run a task if there isn't one running already.
130+
AttemptDequeue();
134131
}
135132

136133
#endregion

0 commit comments

Comments
 (0)