Skip to content

Commit 874b1fd

Browse files
author
Oren (electricessence)
committed
Updated ActionRunner
1 parent 848cdb8 commit 874b1fd

2 files changed

Lines changed: 33 additions & 12 deletions

File tree

ActionRunner.cs

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Threading;
33
using System.Threading.Tasks;
4+
using Open.Threading;
45

56
namespace Open.Threading
67
{
@@ -21,7 +22,7 @@ public static ActionRunner Create(Action action, TaskScheduler scheduler = null)
2122

2223
public static ActionRunner Create<T>(Func<T> action, TaskScheduler scheduler = null)
2324
{
24-
return new ActionRunner(()=> { action(); }, scheduler);
25+
return new ActionRunner(() => { action(); }, scheduler);
2526
}
2627

2728
Action _action;
@@ -69,7 +70,13 @@ public Exception LastFault
6970

7071
public bool Cancel(bool onlyIfNotRunning)
7172
{
72-
return _task?.Value.Cancel(onlyIfNotRunning) ?? false;
73+
var t = _task;
74+
if (t?.Cancel(onlyIfNotRunning) ?? false)
75+
{
76+
Interlocked.CompareExchange(ref _task, null, t);
77+
return true;
78+
}
79+
return false;
7380
}
7481

7582
public bool Cancel()
@@ -95,7 +102,7 @@ public bool IsScheduled
95102
{
96103
get
97104
{
98-
return _task?.Value.IsActive() ?? false;
105+
return _task?.IsActive() ?? false;
99106
}
100107
}
101108

@@ -107,14 +114,14 @@ public void RunSynchronously()
107114
GetAction().Invoke();
108115
}
109116

110-
// Use a Lazy to ensure only 1 time initialization.
111-
Lazy<CancellableTask> _task;
117+
readonly object _taskLock = new object();
118+
CancellableTask _task;
112119
CancellableTask Prepare(TimeSpan delay)
113120
{
114121
LastStart = DateTime.Now;
115122
var task = CancellableTask.Start(GetAction(), delay, _scheduler);
116123
task
117-
.OnFaulted(ex=>
124+
.OnFaulted(ex =>
118125
{
119126
LastFault = ex;
120127
})
@@ -123,8 +130,9 @@ CancellableTask Prepare(TimeSpan delay)
123130
LastComplete = DateTime.Now;
124131
Interlocked.Increment(ref _count);
125132
})
126-
.ContinueWith(t => {
127-
Interlocked.Exchange(ref _task, null);
133+
.ContinueWith(t =>
134+
{
135+
Interlocked.CompareExchange(ref _task, null, task);
128136
});
129137
return task;
130138
}
@@ -136,9 +144,22 @@ public CancellableTask Run()
136144

137145
public CancellableTask Defer(TimeSpan delay, bool clearSchedule = true)
138146
{
139-
if (clearSchedule) Cancel(true); // Don't cancel defered if already running.
140-
var task = LazyInitializer.EnsureInitialized(
141-
ref _task, () => new Lazy<CancellableTask>(()=>Prepare(delay))).Value;
147+
if (clearSchedule)
148+
{
149+
Cancel(true); // Don't cancel defered if already running.
150+
}
151+
CancellableTask task = null;
152+
// Locking seems ugly, but it's difficult to properly synchronize the creation and cleanup of the underlying task without it. :/
153+
// The important part is:
154+
// 1) Only initializing once.
155+
// 2) Allowing for proper cleanup after run which requires a reference to the task.
156+
ThreadSafety.LockConditional(
157+
_taskLock,
158+
() => (task = _task) == null,
159+
() =>
160+
{
161+
Interlocked.Exchange(ref _task, task = Prepare(delay));
162+
});
142163
return task;
143164
}
144165

Open.Threading.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
Part of the "Open" set of libraries.</Description>
1111
<PackageLicenseUrl>https://github.com/electricessence/Open.Threading/blob/master/LISCENSE.md</PackageLicenseUrl>
1212
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
13-
<Version>1.1.3</Version>
13+
<Version>1.1.4</Version>
1414
<AssemblyVersion>1.0.0.1</AssemblyVersion>
1515
<FileVersion>1.0.0.1</FileVersion>
1616
<Copyright>https://github.com/electricessence/Open.Threading/blob/master/LISCENSE.md</Copyright>

0 commit comments

Comments
 (0)