Skip to content

Commit ca929f3

Browse files
author
Jos Hickson
authored
Clean up stop flow (#24)
* Update Travis build. * Clean up stop flow This is partly in reaction to a problem with the actor not showing as stopped if the start work failed. * Try to fix Travis
1 parent b6f32a1 commit ca929f3

9 files changed

Lines changed: 114 additions & 60 deletions

File tree

.travis.yml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,17 @@ branches:
1010
except:
1111
- /^[0-9]/
1212

13-
dotnet: 1.1.6
14-
13+
dotnet: 2.1.4
1514
mono: none
15+
16+
addons:
17+
apt:
18+
sources:
19+
- sourceline: 'deb [arch=amd64] https://packages.microsoft.com/repos/microsoft-ubuntu-trusty-prod trusty main'
20+
key_url: 'https://packages.microsoft.com/keys/microsoft.asc'
21+
packages:
22+
- dotnet-hostfxr-1.0.1
23+
- dotnet-sharedframework-microsoft.netcore.app-1.0.9
1624

1725
script:
1826
- (git fetch --unshallow 2>/dev/null || echo "Already full repo.")

Winton.Extensions.Threading.Actor.Tests.Unit/ActorTests.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -992,6 +992,27 @@ public async Task ShouldBeAbleToPauseActorUntilResumeFromAwaitReturningData(Acto
992992
}
993993
}
994994

995+
[Fact]
996+
public async Task ShutdownShouldReturnImmediatelyIfStartWorkFails()
997+
{
998+
var stopWorkCalled = false;
999+
var actor =
1000+
new Actor
1001+
{
1002+
StartWork = new ActorStartWork(() => throw new Exception("Error.")),
1003+
StopWork = new ActorStopWork(() => stopWorkCalled = true)
1004+
};
1005+
1006+
actor.Awaiting(async x => await x.Start()).ShouldThrow<Exception>().WithMessage("Error.");
1007+
1008+
var stopperThreadId = Thread.CurrentThread.ManagedThreadId;
1009+
1010+
await actor.Stop();
1011+
1012+
Thread.CurrentThread.ManagedThreadId.Should().Be(stopperThreadId);
1013+
stopWorkCalled.Should().BeFalse();
1014+
}
1015+
9951016
[Flags]
9961017
private enum ActorCreateOptions
9971018
{

Winton.Extensions.Threading.Actor.Tests.Unit/Winton.Extensions.Threading.Actor.Tests.Unit.csproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@
1010

1111
<ItemGroup>
1212
<PackageReference Include="FluentAssertions" Version="4.19.4" />
13-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0" />
14-
<PackageReference Include="Moq" Version="4.7.137" />
15-
<PackageReference Include="xunit" Version="2.2.0" />
16-
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
13+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.5.0" />
14+
<PackageReference Include="Moq" Version="4.8.1" />
15+
<PackageReference Include="xunit" Version="2.3.1" />
16+
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
1717
</ItemGroup>
1818

1919
<ItemGroup>

Winton.Extensions.Threading.Actor/Internal/StateMachine/ActiveActorState.cs

Lines changed: 1 addition & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
using System;
2-
using System.Threading;
31
using System.Threading.Tasks;
42

53
namespace Winton.Extensions.Threading.Actor.Internal.StateMachine
@@ -22,44 +20,7 @@ protected override void ScheduleImpl(Task task)
2220

2321
protected override void StopImpl()
2422
{
25-
var finalWork =
26-
(Action)(() =>
27-
{
28-
try
29-
{
30-
Context.StopWork.CancellationToken.ThrowIfCancellationRequested();
31-
Context.StopWork.SyncWork();
32-
}
33-
finally
34-
{
35-
Context.TerminateTaskScheduler();
36-
}
37-
});
38-
39-
var finalTask = Context.ActorTaskFactory.Create(finalWork, CancellationToken.None, Context.StopWork.TaskCreationOptions);
40-
41-
Task.Run(async () =>
42-
{
43-
try
44-
{
45-
await finalTask;
46-
Context.StopCompletionSource.SetResult(true);
47-
}
48-
catch (Exception exception)
49-
{
50-
if (exception is TaskCanceledException)
51-
{
52-
Context.StopCompletionSource.SetCanceled();
53-
}
54-
else
55-
{
56-
Context.StopCompletionSource.SetException(exception);
57-
}
58-
}
59-
});
60-
61-
Context.StartTask(finalTask);
62-
Context.SetState<StoppedActorState>();
23+
Context.SetState<StoppingActorState>();
6324
}
6425

6526
protected override void EnterImpl()

Winton.Extensions.Threading.Actor/Internal/StateMachine/ActorState.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public Task Start()
1616
{
1717
StartImpl();
1818
}
19-
catch (Exception exception)
19+
catch (Exception exception) when (!Context.StartCompletionSource.Task.IsCompleted)
2020
{
2121
Context.StartCompletionSource.SetException(exception);
2222
}
@@ -30,7 +30,7 @@ public Task Stop()
3030
{
3131
StopImpl();
3232
}
33-
catch (Exception exception)
33+
catch (Exception exception) when (!Context.StopCompletionSource.Task.IsCompleted)
3434
{
3535
Context.StopCompletionSource.SetException(exception);
3636
}

Winton.Extensions.Threading.Actor/Internal/StateMachine/InitialActorState.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,7 @@ protected override void StartImpl()
2525

2626
protected override void StopImpl()
2727
{
28-
Context.StartCompletionSource.SetResult(true);
29-
Context.StopCompletionSource.SetResult(true);
30-
Context.SetState<StoppedActorState>();
28+
Context.SetState<StoppingActorState>();
3129
}
3230

3331
protected override void ScheduleImpl(Task task)

Winton.Extensions.Threading.Actor/Internal/StateMachine/StartingActorState.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ protected override void EnterImpl()
5454
Context.StartCompletionSource.SetException(exception);
5555
}
5656

57-
Context.SetState<StoppedActorState>();
57+
Context.SetState<StoppingActorState>();
5858
}
5959
});
6060
}

Winton.Extensions.Threading.Actor/Internal/StateMachine/StoppedActorState.cs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
using System.Linq;
21
using System.Threading.Tasks;
32

43
namespace Winton.Extensions.Threading.Actor.Internal.StateMachine
@@ -25,13 +24,6 @@ protected override void StopImpl()
2524

2625
protected override void EnterImpl()
2726
{
28-
foreach (var task in Context.InitialWorkQueue.Concat(Context.InitialWorkToBeCancelledQueue))
29-
{
30-
task.Cancel();
31-
}
32-
33-
Context.InitialWorkQueue.Clear();
34-
Context.InitialWorkToBeCancelledQueue.Clear();
3527
}
3628
}
3729
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
using System;
2+
using System.Linq;
3+
using System.Threading;
4+
using System.Threading.Tasks;
5+
6+
namespace Winton.Extensions.Threading.Actor.Internal.StateMachine
7+
{
8+
internal sealed class StoppingActorState : ActorState
9+
{
10+
public StoppingActorState(ActorContext context)
11+
: base(context)
12+
{
13+
}
14+
15+
protected override void StartImpl()
16+
{
17+
}
18+
19+
protected override void ScheduleImpl(Task task)
20+
{
21+
task.Cancel();
22+
}
23+
24+
protected override void StopImpl()
25+
{
26+
}
27+
28+
protected override void EnterImpl()
29+
{
30+
var runStopWork = Context.StartCompletionSource.Task.Status == TaskStatus.RanToCompletion;
31+
32+
Context.StartCompletionSource.TrySetResult(true);
33+
34+
var finalWork =
35+
(Action)(() =>
36+
{
37+
try
38+
{
39+
Context.StopWork.CancellationToken.ThrowIfCancellationRequested();
40+
41+
if (runStopWork)
42+
{
43+
Context.StopWork.SyncWork();
44+
}
45+
46+
Context.StopCompletionSource.SetResult(true);
47+
}
48+
catch (OperationCanceledException)
49+
{
50+
Context.StopCompletionSource.SetCanceled();
51+
}
52+
catch (Exception exception)
53+
{
54+
Context.StopCompletionSource.SetException(exception);
55+
}
56+
finally
57+
{
58+
Context.TerminateTaskScheduler();
59+
Context.SetState<StoppedActorState>();
60+
}
61+
});
62+
63+
Context.StartTask(Context.ActorTaskFactory.Create(finalWork, CancellationToken.None, Context.StopWork.TaskCreationOptions));
64+
65+
foreach (var task in Context.InitialWorkQueue.Concat(Context.InitialWorkToBeCancelledQueue))
66+
{
67+
task.Cancel();
68+
}
69+
70+
Context.InitialWorkQueue.Clear();
71+
Context.InitialWorkToBeCancelledQueue.Clear();
72+
}
73+
}
74+
}

0 commit comments

Comments
 (0)