Skip to content

Commit 2297174

Browse files
author
Oren (electricessence)
committed
Initial check.
Separates task-only related classes from Open.Threading.
1 parent 2a2fa61 commit 2297174

7 files changed

Lines changed: 757 additions & 0 deletions

File tree

ActionRunner.cs

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
using System;
2+
using System.Threading;
3+
using System.Threading.Tasks;
4+
using Open.Threading;
5+
6+
namespace Open.Threading.Tasks
7+
{
8+
public class ActionRunner : ICancellable
9+
{
10+
public ActionRunner(Action action, TaskScheduler scheduler = null)
11+
{
12+
_action = action;
13+
_scheduler = scheduler; // No need to hold a refernce to the default, just keep it null.
14+
LastStart = DateTime.MaxValue;
15+
LastComplete = DateTime.MaxValue;
16+
}
17+
18+
public static ActionRunner Create(Action action, TaskScheduler scheduler = null)
19+
{
20+
return new ActionRunner(action, scheduler);
21+
}
22+
23+
public static ActionRunner Create<T>(Func<T> action, TaskScheduler scheduler = null)
24+
{
25+
return new ActionRunner(() => { action(); }, scheduler);
26+
}
27+
28+
Action _action;
29+
protected TaskScheduler _scheduler;
30+
31+
protected int _count;
32+
public int Count
33+
{
34+
get { return _count; }
35+
}
36+
37+
public DateTime LastStart
38+
{
39+
get;
40+
protected set;
41+
}
42+
43+
public bool HasBeenRun
44+
{
45+
get
46+
{
47+
return LastStart < DateTime.Now;
48+
}
49+
}
50+
51+
public DateTime LastComplete
52+
{
53+
get;
54+
protected set;
55+
}
56+
57+
public bool HasCompleted
58+
{
59+
get
60+
{
61+
return LastComplete < DateTime.Now;
62+
}
63+
}
64+
65+
public Exception LastFault
66+
{
67+
get;
68+
protected set;
69+
}
70+
71+
public bool Cancel(bool onlyIfNotRunning)
72+
{
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;
80+
}
81+
82+
public bool Cancel()
83+
{
84+
return Cancel(false);
85+
}
86+
87+
public void Dispose()
88+
{
89+
Cancel();
90+
_action = null;
91+
}
92+
93+
Action GetAction()
94+
{
95+
var a = _action;
96+
if (a == null)
97+
throw new ObjectDisposedException(typeof(ActionRunner).ToString());
98+
return a;
99+
}
100+
101+
public bool IsScheduled
102+
{
103+
get
104+
{
105+
return _task?.IsActive() ?? false;
106+
}
107+
}
108+
109+
/// <summary>
110+
/// Indiscriminately invokes the action.
111+
/// </summary>
112+
public void RunSynchronously()
113+
{
114+
GetAction().Invoke();
115+
}
116+
117+
readonly object _taskLock = new object();
118+
CancellableTask _task;
119+
CancellableTask Prepare()
120+
{
121+
LastStart = DateTime.Now;
122+
var task = CancellableTask.Init(GetAction());
123+
task
124+
.OnFaulted(ex =>
125+
{
126+
LastFault = ex;
127+
})
128+
.OnFullfilled(() =>
129+
{
130+
LastComplete = DateTime.Now;
131+
Interlocked.Increment(ref _count);
132+
})
133+
.ContinueWith(t =>
134+
{
135+
Interlocked.CompareExchange(ref _task, null, task);
136+
});
137+
return task;
138+
}
139+
140+
public CancellableTask Run()
141+
{
142+
return Defer(TimeSpan.Zero);
143+
}
144+
145+
public CancellableTask Defer(TimeSpan delay, bool clearSchedule = true)
146+
{
147+
if (clearSchedule)
148+
{
149+
Cancel(true); // Don't cancel defered if already running.
150+
}
151+
152+
CancellableTask task = null;
153+
if((task = _task) == null)
154+
{
155+
task = Prepare();
156+
if(task==Interlocked.CompareExchange(ref _task, task, null))
157+
{
158+
task.Start(delay);
159+
}
160+
}
161+
return task;
162+
}
163+
164+
public CancellableTask Defer(int millisecondsDelay, bool clearSchedule = true)
165+
{
166+
return Defer(TimeSpan.FromMilliseconds(millisecondsDelay), clearSchedule);
167+
}
168+
169+
}
170+
}

CancellableTask.cs

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
using System;
2+
using System.Threading;
3+
using System.Threading.Tasks;
4+
5+
namespace Open.Threading.Tasks
6+
{
7+
/// <summary>
8+
/// A Task sub-class that simplifies cancelling.
9+
/// </summary>
10+
public class CancellableTask : Task, ICancellable
11+
{
12+
protected CancellationTokenSource TokenSource;
13+
14+
public bool Cancel(bool onlyIfNotRunning)
15+
{
16+
var ts = Interlocked.Exchange(ref TokenSource, null); // Cancel can only be called once.
17+
18+
if (ts == null || ts.IsCancellationRequested || IsCanceled || IsFaulted || IsCompleted)
19+
return false;
20+
21+
var isRunning = Status == TaskStatus.Running;
22+
if (!onlyIfNotRunning || !isRunning)
23+
ts.Cancel();
24+
25+
return !isRunning;
26+
}
27+
28+
public bool Cancel()
29+
{
30+
return Cancel(false);
31+
}
32+
33+
protected static void Blank() { }
34+
35+
public void Dispose()
36+
{
37+
Cancel();
38+
}
39+
40+
protected CancellableTask(Action action, CancellationToken token)
41+
: base(action ?? Blank)
42+
{
43+
}
44+
45+
protected CancellableTask(Action action)
46+
: base(action ?? Blank)
47+
{
48+
}
49+
50+
protected CancellableTask(CancellationToken token)
51+
: this(Blank, token)
52+
{
53+
}
54+
55+
protected CancellableTask()
56+
: this(Blank)
57+
{
58+
}
59+
60+
// Only allow for static initilialization because this owns the TokenSource.
61+
public static CancellableTask Init(Action action = null)
62+
{
63+
var ts = new CancellationTokenSource();
64+
var token = ts.Token;
65+
return new CancellableTask(action, token)
66+
{
67+
TokenSource = ts // Could potentially call cancel before run actually happens.
68+
};
69+
}
70+
71+
public void Start(TimeSpan delay, TaskScheduler scheduler = null)
72+
{
73+
if (delay < TimeSpan.Zero)
74+
{
75+
RunSynchronously();
76+
}
77+
else if (delay == TimeSpan.Zero)
78+
{
79+
if (scheduler == null)
80+
Start();
81+
else
82+
Start(scheduler);
83+
}
84+
else
85+
{
86+
int runState = 0;
87+
88+
ContinueWith(t =>
89+
{
90+
// If this is arbitrarily run before the delay, then cancel the delay.
91+
if (Interlocked.Increment(ref runState) < 2)
92+
Cancel();
93+
});
94+
95+
Delay(delay, TokenSource.Token)
96+
.OnFullfilled(() =>
97+
{
98+
Interlocked.Increment(ref runState);
99+
this.EnsureStarted(scheduler);
100+
});
101+
}
102+
}
103+
104+
public void Start(int millisecondsDelay, TaskScheduler scheduler = null)
105+
{
106+
Start(TimeSpan.FromMilliseconds(millisecondsDelay), scheduler);
107+
}
108+
109+
public static CancellableTask StartNew(TimeSpan delay, Action action = null, TaskScheduler scheduler = null)
110+
{
111+
var task = new CancellableTask(action);
112+
task.Start(delay, scheduler);
113+
return task;
114+
}
115+
116+
public static CancellableTask StartNew(int millisecondsDelay, Action action = null)
117+
{
118+
return StartNew(TimeSpan.FromMilliseconds(millisecondsDelay), action);
119+
}
120+
121+
public static CancellableTask StartNew(Action action, TimeSpan? delay = null, TaskScheduler scheduler = null)
122+
{
123+
return StartNew(delay ?? TimeSpan.Zero, action, scheduler);
124+
}
125+
}
126+
127+
}

ICancellable.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System;
2+
3+
namespace Open.Threading.Tasks
4+
{
5+
public interface ICancellable : IDisposable
6+
{
7+
/// <summary>
8+
/// Returns true if cancelled.
9+
/// Returns false if already run or already cancelled or unable to cancel.
10+
/// </summary>
11+
/// <returns></returns>
12+
bool Cancel();
13+
}
14+
}

Open.Threading.Tasks.csproj

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netcoreapp1.1</TargetFramework>
5+
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
6+
<Authors>electricessence</Authors>
7+
<Company>electricessence</Company>
8+
<Description>A set of utilities and extensions for working with Tasks.
9+
10+
Part of the "Open" set of libraries.</Description>
11+
<Copyright>https://github.com/electricessence/Open.Threading.Tasks/blob/master/LISCENSE.md</Copyright>
12+
<PackageProjectUrl>https://github.com/electricessence/Open.Threading.Tasks/</PackageProjectUrl>
13+
<RepositoryUrl>https://github.com/electricessence/Open.Threading.Tasks/</RepositoryUrl>
14+
<PackageLicenseUrl>https://github.com/electricessence/Open.Threading.Tasks/blob/master/LISCENSE.md</PackageLicenseUrl>
15+
<RepositoryType>git</RepositoryType>
16+
<PackageTags>dotnet, dotnet-core, dotnetcore, cs, extensions, actionrunner, cancellable, cancellabletask, progress, task-extensions</PackageTags>
17+
</PropertyGroup>
18+
19+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
20+
<Optimize>True</Optimize>
21+
</PropertyGroup>
22+
23+
<ItemGroup>
24+
<None Remove=".git" />
25+
<None Remove=".gitignore" />
26+
<None Remove="LICENSE" />
27+
<None Remove="README.md" />
28+
</ItemGroup>
29+
30+
</Project>

0 commit comments

Comments
 (0)