Skip to content
This repository was archived by the owner on Dec 22, 2019. It is now read-only.

Commit c032313

Browse files
committed
Implementing a child task scheduler.
1 parent 32b62cf commit c032313

12 files changed

Lines changed: 264 additions & 58 deletions

File tree

UpdateLib/TestApp/Form1.Designer.cs

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

UpdateLib/TestApp/Form1.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using System.Text;
1111
using System.Threading;
1212
using System.Windows.Forms;
13+
using TestApp.Testing;
1314

1415
namespace TestApp
1516
{
@@ -75,7 +76,7 @@ private string ReadFile(string file)
7576

7677
string[] lines = File.ReadAllLines(file);
7778

78-
return string.Join(", ", lines);
79+
return string.Join(", ", lines);
7980
}
8081

8182
/// <summary>
@@ -95,5 +96,12 @@ private string ReadFileAndKeepStreamOpen(string file)
9596

9697
return text;
9798
}
99+
100+
private void button1_Click(object sender, EventArgs e)
101+
{
102+
DummyTask task = new DummyTask();
103+
task.TaskCompleted += (o, ex) => Logger.Debug(nameof(DummyTask), "Callback task completed!");
104+
task.Start();
105+
}
98106
}
99107
}

UpdateLib/TestApp/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ static void Main()
2626

2727
private static void InitializeUpdater()
2828
{
29-
Updater.Instance.Initialize();
29+
//Updater.Instance.Initialize();
3030
Updater.Instance.UpdateURL = "https://raw.githubusercontent.com/MatthiWare/UpdateLib.TestApp.UpdateExample/master/Dev/updatefile.xml";
3131
//Updater.Instance.UpdateURL = "http://matthiware.dev/UpdateLib/Dev/updatefile.xml";
3232
}

UpdateLib/TestApp/TestApp.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
</Compile>
5555
<Compile Include="Program.cs" />
5656
<Compile Include="Properties\AssemblyInfo.cs" />
57+
<Compile Include="Testing\DummyTask.cs" />
5758
<EmbeddedResource Include="Form1.resx">
5859
<DependentUpon>Form1.cs</DependentUpon>
5960
</EmbeddedResource>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
using MatthiWare.UpdateLib.Logging;
2+
using MatthiWare.UpdateLib.Tasks;
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Text;
7+
using System.Threading;
8+
9+
namespace TestApp.Testing
10+
{
11+
public class DummyTask : AsyncTask
12+
{
13+
protected override void DoWork()
14+
{
15+
for (int i = 0; i < 10; i++)
16+
{
17+
Enqueue(new Action<int>(ChildWorkStuff), i);
18+
}
19+
}
20+
21+
Random rnd = new Random(DateTime.Now.Millisecond);
22+
23+
private void ChildWorkStuff(int id)
24+
{
25+
int waitTime = rnd.Next(1000, 5000);
26+
27+
Thread.Sleep(waitTime);
28+
29+
Logger.Debug(nameof(ChildWorkStuff), $"Task[{id.ToString("X2")}] Completed");
30+
}
31+
32+
33+
}
34+
}

UpdateLib/UpdateLib.Generator/Tasks/UpdateGeneratorTask.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ protected override void DoWork()
5454

5555
private void AddDirRecursive(GenFolder dir, DirectoryEntry entry)
5656
{
57-
Logger.Debug(GetType().Name, $"Thread: {Thread.CurrentThread.ManagedThreadId}");
57+
// Logger.Debug(GetType().Name, $"Thread: {Thread.CurrentThread.ManagedThreadId}");
5858

5959
List<GenFile> files = dir.Files;
6060
foreach (GenFile genFile in files)
@@ -79,10 +79,10 @@ private void AddDirRecursive(GenFolder dir, DirectoryEntry entry)
7979
DirectoryEntry newEntry = new DirectoryEntry(string.IsNullOrEmpty(newDir.PathVariable) ? newDir.Name : newDir.PathVariable);
8080
entry.Directories.Add(newEntry);
8181

82-
//AddDirRecursiveDelegate caller = new AddDirRecursiveDelegate(AddDirRecursive);
83-
//Enqueue(caller, newDir, newEntry);
82+
AddDirRecursiveDelegate caller = new AddDirRecursiveDelegate(AddDirRecursive);
83+
Enqueue(caller, newDir, newEntry);
8484

85-
AddDirRecursive(newDir, newEntry);
85+
//AddDirRecursive(newDir, newEntry);
8686
}
8787
}
8888
}

UpdateLib/UpdateLib.Generator/UI/Pages/BuilderPage.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ private AsyncTask<UpdateFile> Build(Stream s)
8585

8686
Logger.Debug(GetType().Name, $"File generation completed in {sw.ElapsedMilliseconds} ms.");
8787

88-
sw.Reset();
88+
8989

9090
btnBuild.Enabled = true;
9191

@@ -119,13 +119,15 @@ private AsyncTask<UpdateFile> Build(Stream s)
119119
ParentForm,
120120
"Builder",
121121
"Build completed",
122-
"The update file has been succesfully generated!",
122+
"The update file has been succesfully generated!\n" +
123+
$"File generation completed in {sw.ElapsedMilliseconds} ms.",
123124
SystemIcons.Information,
124125
MessageBoxButtons.OK);
125126
};
126127

127128
lblStatus.Text = "Status: Building..";
128129

130+
sw.Reset();
129131
sw.Start();
130132

131133
return task.Start();

UpdateLib/UpdateLib.Tests/Tasks/AsyncTaskTest.cs

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -36,39 +36,35 @@ public void CancelledTaskIsCancelled()
3636
public void FaultyTaskReturnsException()
3737
{
3838
ErrorTask task = new ErrorTask();
39-
ManualResetEvent wait = new ManualResetEvent(false);
39+
//ManualResetEvent wait = new ManualResetEvent(false);
4040

4141
task.TaskCompleted += (o, e) =>
4242
{
4343
Assert.False(e.Cancelled, "The task got cancelled");
4444
Assert.NotNull(e.Error, "The error object is null");
45-
Assert.IsInstanceOf<AsyncTaskTestException>(e.Error, $"{e.Error} is not an instance of {nameof(AsyncTaskTestException)}");
46-
wait.Set();
45+
Assert.IsInstanceOf<ErrorTask>(e.Error, $"{e.Error} is not an instance of {nameof(AsyncTaskTestException)}");
46+
// wait.Set();
4747
};
4848
task.Start();
4949
task.AwaitTask();
50-
wait.WaitOne();
50+
//wait.WaitOne();
5151
}
5252

5353
[Test, Parallelizable]
5454
public void TestMethod()
5555
{
56-
Object o = new Object();
57-
ResultTask<Object> task = new ResultTask<Object>(o);
58-
task.TaskCompleted += (s, e) =>
59-
{
60-
Assert.Fail("lol");
61-
};
56+
object o = new object();
57+
ResultTask<object> task = new ResultTask<object>(o);
6258
task.Start();
63-
Assert.AreEqual(o, task.AwaitTask());
59+
Assert.AreEqual(o, task.AwaitTask());
6460
}
6561

6662
[Test, Parallelizable]
6763
public void TestResultReturnsCorrectObject()
6864
{
6965
TestResultTask<bool>(false);
7066
TestResultTask<int>(19951);
71-
TestResultTask<Object>(new Object());
67+
TestResultTask<object>(new object());
7268
}
7369

7470
private void TestResultTask<T>(T input)
@@ -117,13 +113,13 @@ protected override void DoWork()
117113
Action simulation = new Action(() =>
118114
{
119115
Thread.Sleep(1000);
120-
value = false;
116+
value = true;
121117
});
122118

123-
Enqueue(simulation);
124-
125119
Assert.IsFalse(value);
126120

121+
Enqueue(simulation);
122+
127123
AwaitWorkers();
128124

129125
Assert.IsTrue(value);
@@ -175,13 +171,13 @@ protected override void DoWork()
175171
Result = returnObj;
176172
});
177173

178-
Enqueue(call,200);
174+
Enqueue(call, 200);
179175
}
180176
}
181177

182178
private class AsyncTaskTestException : Exception
183179
{
184-
public AsyncTaskTestException():base("Test Exception")
180+
public AsyncTaskTestException() : base("Test Exception")
185181
{
186182
}
187183

UpdateLib/UpdateLib/Tasks/AsyncTask.cs

Lines changed: 35 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using MatthiWare.UpdateLib.Logging;
2+
using MatthiWare.UpdateLib.Threading;
23
using System;
34
using System.Collections.Generic;
45
using System.ComponentModel;
@@ -20,12 +21,14 @@ public abstract class AsyncTask
2021
private bool m_useSyncContext = true;
2122
private SynchronizationContext m_syncContext;
2223

24+
public bool IsChildTask { get; internal set; } = false;
25+
2326
#if DEBUG
2427
public Stopwatch m_sw = new Stopwatch();
2528
#endif
2629

27-
private readonly Queue<WaitHandle> waitQueue = new Queue<WaitHandle>();
28-
private WaitHandle mainWait;
30+
private readonly ConcurrentQueue<AsyncTask> m_childTasks = new ConcurrentQueue<AsyncTask>();
31+
private ManualResetEvent m_waitHandle = new ManualResetEvent(true);
2932
private readonly object sync = new object();
3033

3134
private bool m_running = false;
@@ -165,8 +168,8 @@ private void Reset()
165168
LastException = null;
166169
IsCompleted = false;
167170

168-
mainWait = null;
169-
waitQueue.Clear();
171+
m_waitHandle.Reset();
172+
m_childTasks.Clear();
170173

171174
#if DEBUG
172175
m_sw.Reset();
@@ -211,7 +214,7 @@ public AsyncTask Start()
211214
m_sw.Start();
212215
#endif
213216

214-
mainWait = worker.BeginInvoke(new AsyncCallback((IAsyncResult r) =>
217+
worker.BeginInvoke(new AsyncCallback((IAsyncResult r) =>
215218
{
216219
#if DEBUG
217220
m_sw.Stop();
@@ -221,7 +224,9 @@ public AsyncTask Start()
221224

222225
OnTaskCompleted(m_lastException, IsCancelled);
223226

224-
}), null).AsyncWaitHandle;
227+
m_waitHandle.Set();
228+
229+
}), null); ;
225230

226231
return this;
227232
}
@@ -247,27 +252,25 @@ public virtual void Cancel()
247252
/// <param name="args">Optional arguments for the action</param>
248253
protected void Enqueue(Delegate action, params object[] args)
249254
{
250-
Action subTaskAction = new Action(() =>
255+
// Don't allow to start another task when the parent task has been cancelled or contains errors.
256+
if (HasErrors || IsCancelled)
257+
return;
258+
259+
AsyncTask task = AsyncTaskFactory.From(action, args);
260+
task.IsChildTask = true;
261+
task.TaskCompleted += (o, e) =>
251262
{
252-
try
263+
if (e.Error != null)
253264
{
254-
action.DynamicInvoke(args);
255-
}
256-
catch (Exception ex)
257-
{
258-
LastException = ex?.InnerException ?? ex;
265+
LastException = e.Error?.InnerException ?? e.Error;
259266

260267
Logger.Error(GetType().Name, LastException);
261268
}
262-
});
269+
};
263270

264-
// Don't allow to start another task when the parent task has been cancelled or contains errors.
265-
if (HasErrors || IsCancelled)
266-
return;
271+
m_childTasks.Enqueue(task);
267272

268-
IAsyncResult result = subTaskAction.BeginInvoke(new AsyncCallback(r => { subTaskAction.EndInvoke(r); }), null);
269-
lock (sync)
270-
waitQueue.Enqueue(result.AsyncWaitHandle);
273+
WorkerScheduler.Instance.Schedule(task);
271274
}
272275

273276
/// <summary>
@@ -276,28 +279,27 @@ protected void Enqueue(Delegate action, params object[] args)
276279
/// </summary>
277280
public void AwaitTask()
278281
{
279-
if (mainWait != null)
282+
if (IsChildTask && !IsCompleted && !IsRunning)
283+
Reset();
284+
285+
if (m_waitHandle != null)
280286
{
281-
mainWait.WaitOne();
282-
mainWait.Close();
283-
mainWait = null;
287+
m_waitHandle.WaitOne();
288+
m_waitHandle.Close();
289+
m_waitHandle = null;
284290
}
285291
}
286292

293+
private int x = 0;
294+
287295
/// <summary>
288296
/// Blocks the calling thread until all the workers are done.
289297
/// </summary>
290298
protected void AwaitWorkers()
291299
{
292-
while (waitQueue.Count > 0)
293-
{
294-
WaitHandle wh = null;
295-
lock (sync)
296-
wh = waitQueue.Dequeue();
297-
298-
wh.WaitOne();
299-
wh.Close();
300-
}
300+
AsyncTask task = null;
301+
while (m_childTasks.TryDequeue(out task))
302+
task.AwaitTask();
301303
}
302304

303305
/// <summary>

0 commit comments

Comments
 (0)