Skip to content

Commit 4e70449

Browse files
author
Oren (electricessence)
committed
Improved extensions handling with added source references.
1 parent 1e943cd commit 4e70449

File tree

6 files changed

+84
-88
lines changed

6 files changed

+84
-88
lines changed

ActionRunner.cs

Lines changed: 19 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,23 @@ namespace Open.Threading.Tasks
66
{
77
public class ActionRunner : ICancellable
88
{
9-
public ActionRunner(Action action, TaskScheduler scheduler = null)
9+
public ActionRunner(Action action, TaskScheduler? scheduler = default)
1010
{
1111
_action = action;
1212
_scheduler = scheduler; // No need to hold a refernce to the default, just keep it null.
1313
LastStart = DateTime.MaxValue;
1414
LastComplete = DateTime.MaxValue;
1515
}
1616

17-
public static ActionRunner Create(Action action, TaskScheduler scheduler = null)
18-
{
19-
return new ActionRunner(action, scheduler);
20-
}
17+
public static ActionRunner Create(Action action, TaskScheduler? scheduler = default)
18+
=> new ActionRunner(action, scheduler);
2119

22-
public static ActionRunner Create<T>(Func<T> action, TaskScheduler scheduler = null)
23-
{
24-
return new ActionRunner(() => { action(); }, scheduler);
25-
}
20+
public static ActionRunner Create<T>(Func<T> action, TaskScheduler? scheduler = default)
21+
=> new ActionRunner(() => { action(); }, scheduler);
2622

27-
Action _action;
23+
Action? _action;
2824
// ReSharper disable once NotAccessedField.Global
29-
protected TaskScheduler _scheduler;
25+
protected TaskScheduler? _scheduler;
3026

3127
protected int _count;
3228
public int Count => _count;
@@ -70,7 +66,7 @@ public void Dispose()
7066
Action GetAction()
7167
{
7268
var a = _action;
73-
if (a == null)
69+
if (a is null)
7470
throw new ObjectDisposedException(typeof(ActionRunner).ToString());
7571
return a;
7672
}
@@ -85,33 +81,27 @@ public void RunSynchronously()
8581
GetAction().Invoke();
8682
}
8783

88-
// ReSharper disable once UnusedMember.Local
89-
readonly object _taskLock = new object();
90-
CancellableTask _task;
84+
CancellableTask? _task;
9185
CancellableTask Prepare()
9286
{
9387
LastStart = DateTime.Now;
9488
var task = CancellableTask.Init(GetAction());
9589
task
96-
.OnFaulted(ex =>
97-
{
98-
})
99-
.OnFullfilled(() =>
100-
{
101-
LastComplete = DateTime.Now;
102-
Interlocked.Increment(ref _count);
103-
})
10490
.ContinueWith(t =>
10591
{
92+
if (t.Status == TaskStatus.RanToCompletion)
93+
{
94+
LastComplete = DateTime.Now;
95+
Interlocked.Increment(ref _count);
96+
}
10697
Interlocked.CompareExchange(ref _task, null, task);
107-
});
98+
},
99+
TaskContinuationOptions.ExecuteSynchronously);
108100
return task;
109101
}
110102

111103
public CancellableTask Run()
112-
{
113-
return Defer(TimeSpan.Zero);
114-
}
104+
=> Defer(TimeSpan.Zero);
115105

116106
public CancellableTask Defer(TimeSpan delay, bool clearSchedule = true)
117107
{
@@ -120,7 +110,7 @@ public CancellableTask Defer(TimeSpan delay, bool clearSchedule = true)
120110
Cancel(true); // Don't cancel defered if already running.
121111
}
122112

123-
CancellableTask task;
113+
CancellableTask? task;
124114
if ((task = _task) != null) return task;
125115
task = Prepare();
126116
if (null == Interlocked.CompareExchange(ref _task, task, null))
@@ -131,9 +121,7 @@ public CancellableTask Defer(TimeSpan delay, bool clearSchedule = true)
131121
}
132122

133123
public CancellableTask Defer(int millisecondsDelay, bool clearSchedule = true)
134-
{
135-
return Defer(TimeSpan.FromMilliseconds(millisecondsDelay), clearSchedule);
136-
}
124+
=> Defer(TimeSpan.FromMilliseconds(millisecondsDelay), clearSchedule);
137125

138126
}
139127
}

CancellableTask.cs

Lines changed: 24 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -9,26 +9,25 @@ namespace Open.Threading.Tasks
99
/// </summary>
1010
public class CancellableTask : Task, ICancellable
1111
{
12-
protected CancellationTokenSource TokenSource;
12+
protected CancellationTokenSource? TokenSource;
1313

1414
public bool Cancel(bool onlyIfNotRunning)
1515
{
16-
var ts = Interlocked.Exchange(ref TokenSource, null); // Cancel can only be called once.
17-
18-
if (ts == null || ts.IsCancellationRequested || IsCanceled || IsFaulted || IsCompleted)
16+
if (onlyIfNotRunning && Status == TaskStatus.Running || IsCanceled || IsFaulted || IsCompleted)
1917
return false;
2018

21-
var isRunning = Status == TaskStatus.Running;
22-
if (!onlyIfNotRunning || !isRunning)
19+
var ts = Interlocked.Exchange(ref TokenSource, null); // Cancel can only be called once.
20+
if (ts is null) return false;
21+
using (ts)
22+
{
23+
if (ts.IsCancellationRequested) return false;
2324
ts.Cancel();
25+
}
2426

25-
return !isRunning;
27+
return true;
2628
}
2729

28-
public bool Cancel()
29-
{
30-
return Cancel(false);
31-
}
30+
public bool Cancel() => Cancel(false);
3231

3332
protected static void Blank() { }
3433

@@ -38,12 +37,12 @@ protected override void Dispose(bool disposing)
3837
base.Dispose(disposing);
3938
}
4039

41-
protected CancellableTask(Action action, CancellationToken token)
40+
protected CancellableTask(Action? action, CancellationToken token)
4241
: base(action ?? Blank, token)
4342
{
4443
}
4544

46-
protected CancellableTask(Action action)
45+
protected CancellableTask(Action? action)
4746
: base(action ?? Blank)
4847
{
4948
}
@@ -59,7 +58,7 @@ protected CancellableTask()
5958
}
6059

6160
// Only allow for static initilialization because this owns the TokenSource.
62-
public static CancellableTask Init(Action action = null)
61+
public static CancellableTask Init(Action? action = null)
6362
{
6463
var ts = new CancellationTokenSource();
6564
var token = ts.Token;
@@ -69,15 +68,15 @@ public static CancellableTask Init(Action action = null)
6968
};
7069
}
7170

72-
public void Start(TimeSpan delay, TaskScheduler scheduler = null)
71+
public void Start(TimeSpan delay, TaskScheduler? scheduler = null)
7372
{
7473
if (delay < TimeSpan.Zero)
7574
{
7675
RunSynchronously();
7776
}
7877
else if (delay == TimeSpan.Zero)
7978
{
80-
if (scheduler == null)
79+
if (scheduler is null)
8180
Start();
8281
else
8382
Start(scheduler);
@@ -93,7 +92,7 @@ public void Start(TimeSpan delay, TaskScheduler scheduler = null)
9392
Cancel();
9493
});
9594

96-
Delay(delay, TokenSource.Token)
95+
Delay(delay, TokenSource!.Token)
9796
.OnFullfilled(() =>
9897
{
9998
Interlocked.Increment(ref runState);
@@ -102,29 +101,23 @@ public void Start(TimeSpan delay, TaskScheduler scheduler = null)
102101
}
103102
}
104103

105-
public void Start(int millisecondsDelay, TaskScheduler scheduler = null)
106-
{
107-
Start(TimeSpan.FromMilliseconds(millisecondsDelay), scheduler);
108-
}
104+
public void Start(int millisecondsDelay, TaskScheduler? scheduler = default)
105+
=> Start(TimeSpan.FromMilliseconds(millisecondsDelay), scheduler);
109106

110-
public static CancellableTask StartNew(TimeSpan delay, Action action = null, TaskScheduler scheduler = null)
107+
public static CancellableTask StartNew(TimeSpan delay, Action? action = null, TaskScheduler? scheduler = default)
111108
{
112109
var task = new CancellableTask(action);
113110
task.Start(delay, scheduler);
114111
return task;
115112
}
116113

117-
public static CancellableTask StartNew(int millisecondsDelay, Action action = null)
118-
{
119-
return StartNew(TimeSpan.FromMilliseconds(millisecondsDelay), action);
120-
}
114+
public static CancellableTask StartNew(int millisecondsDelay, Action? action = null)
115+
=> StartNew(TimeSpan.FromMilliseconds(millisecondsDelay), action);
121116

122-
public static CancellableTask StartNew(Action action, TimeSpan? delay = null, TaskScheduler scheduler = null)
123-
{
124-
return StartNew(delay ?? TimeSpan.Zero, action, scheduler);
125-
}
117+
public static CancellableTask StartNew(Action action, TimeSpan? delay = null, TaskScheduler? scheduler = default)
118+
=> StartNew(delay ?? TimeSpan.Zero, action, scheduler);
126119

127-
public static CancellableTask StartNew(Action<CancellationToken> action, TimeSpan? delay = null, TaskScheduler scheduler = null)
120+
public static CancellableTask StartNew(Action<CancellationToken> action, TimeSpan? delay = null, TaskScheduler? scheduler = default)
128121
{
129122
var ts = new CancellationTokenSource();
130123
var token = ts.Token;

Open.Threading.Tasks.csproj

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,41 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>netstandard2.0</TargetFramework>
4+
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
5+
<LangVersion>latest</LangVersion>
6+
<Nullable>enable</Nullable>
57
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
68
<Authors>electricessence</Authors>
7-
<Company>electricessence</Company>
89
<Description>A set of utilities and extensions for working with Tasks.
910

1011
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>
12+
<Copyright>https://github.com/Open-NET-Libraries/Open.Threading.Tasks/blob/master/LISCENSE.md</Copyright>
13+
<PackageProjectUrl>https://github.com/Open-NET-Libraries/Open.Threading.Tasks/</PackageProjectUrl>
14+
<RepositoryUrl>https://github.com/Open-NET-Libraries/Open.Threading.Tasks/</RepositoryUrl>
1415
<PackageLicenseExpression>MIT</PackageLicenseExpression>
1516
<RepositoryType>git</RepositoryType>
1617
<PackageTags>dotnet, dotnet-core, dotnetcore, cs, extensions, actionrunner, cancellable, cancellabletask, progress, task-extensions</PackageTags>
17-
<Version>1.1.3</Version>
18+
<Version>1.2.0</Version>
1819
<PackageReleaseNotes></PackageReleaseNotes>
20+
<PackageIcon>logo.png</PackageIcon>
21+
<PublishRepositoryUrl>true</PublishRepositoryUrl>
22+
<IncludeSymbols>true</IncludeSymbols>
23+
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
1924
</PropertyGroup>
2025

21-
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
22-
<Optimize>True</Optimize>
23-
<LangVersion>latest</LangVersion>
24-
</PropertyGroup>
25-
26-
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
27-
<LangVersion>latest</LangVersion>
28-
</PropertyGroup>
26+
<ItemGroup>
27+
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
28+
</ItemGroup>
2929

3030
<ItemGroup>
3131
<None Remove=".git" />
3232
<None Remove=".gitignore" />
3333
<None Remove="LICENSE" />
3434
<None Remove="README.md" />
35+
<None Include="logo.png">
36+
<Pack>True</Pack>
37+
<PackagePath></PackagePath>
38+
</None>
3539
</ItemGroup>
3640

3741
</Project>

TaskExtensions.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public static class TaskExtensions
1010
/// </summary>
1111
public static bool IsActive(this Task target)
1212
{
13-
if (target == null)
13+
if (target is null)
1414
throw new NullReferenceException();
1515

1616
switch (target.Status)
@@ -36,14 +36,14 @@ public static bool IsActive(this Task target)
3636
/// <param name="target">The task to ensure start.</param>
3737
/// <param name="scheduler">Optional scheduler to use.</param>
3838
/// <returns>True if start attempt was successful.</returns>
39-
public static bool EnsureStarted(this Task target, TaskScheduler scheduler = null)
39+
public static bool EnsureStarted(this Task target, TaskScheduler? scheduler = default)
4040
{
41-
if (target == null) throw new NullReferenceException();
41+
if (target is null) throw new NullReferenceException();
4242

4343
if (target.Status != TaskStatus.Created) return false;
4444
try
4545
{
46-
if (scheduler == null)
46+
if (scheduler is null)
4747
target.Start();
4848
else
4949
target.Start(scheduler);

TimeoutHandler.cs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,30 +6,41 @@ namespace Open.Threading.Tasks
66
{
77
public class TimeoutHandler : IDisposable
88
{
9-
readonly CancellationTokenSource TokenSource;
10-
TimeoutHandler(int delay, Action<int> onComplete)
9+
CancellationTokenSource? TokenSource;
10+
TimeoutHandler(TimeSpan delay, Action<TimeSpan> onTimeout)
1111
{
1212
TokenSource = new CancellationTokenSource();
1313
Task.Delay(delay, TokenSource.Token).ContinueWith(t =>
1414
{
15-
if (!t.IsCanceled) onComplete(delay);
15+
Interlocked.Exchange(ref TokenSource, null)?.Dispose();
16+
if (!t.IsCanceled) onTimeout(delay);
1617
});
1718
}
1819

19-
public static TimeoutHandler New(int delay, Action<int> onComplete)
20+
public static TimeoutHandler New(TimeSpan delay, Action<TimeSpan> onTimeout)
21+
=> new TimeoutHandler(delay, onTimeout);
22+
23+
public static bool New(TimeSpan delay, out IDisposable timeout, Action<TimeSpan> onTimeout)
2024
{
21-
return new TimeoutHandler(delay, onComplete);
25+
timeout = New(delay, onTimeout);
26+
return true;
2227
}
2328

24-
public static bool New(int delay, out IDisposable timeout, Action<int> onComplete)
29+
public static TimeoutHandler New(double delay, Action<double> onTimeout)
30+
=> New(TimeSpan.FromMilliseconds(delay), ts=> onTimeout(ts.TotalMilliseconds));
31+
32+
public static bool New(double delay, out IDisposable timeout, Action<double> onTimeout)
2533
{
26-
timeout = New(delay, onComplete);
34+
timeout = New(delay, onTimeout);
2735
return true;
2836
}
2937

3038
public void Dispose()
3139
{
32-
TokenSource.Cancel();
40+
var ts = Interlocked.Exchange(ref TokenSource, null);
41+
if (ts is null) return;
42+
ts.Cancel();
43+
ts.Dispose();
3344
}
3445
}
3546
}

logo.png

54.6 KB
Loading

0 commit comments

Comments
 (0)