mirror of
https://github.com/chylex/Discord-History-Tracker.git
synced 2025-06-05 01:43:22 +03:00
Add support for cancelling async value computation
This commit is contained in:
parent
15e8b9da63
commit
b13b85dedd
@ -153,7 +153,7 @@ namespace DHT.Desktop.Main.Controls {
|
||||
else {
|
||||
exportedMessageCount = null;
|
||||
UpdateFilterStatisticsText();
|
||||
exportedMessageCountComputer.Compute(_ => db.CountMessages(filter));
|
||||
exportedMessageCountComputer.Compute(() => db.CountMessages(filter));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using DHT.Server.Data;
|
||||
using DHT.Server.Data.Filters;
|
||||
@ -458,7 +457,7 @@ LEFT JOIN replied_to rt ON m.message_id = rt.message_id" + filter.GenerateWhereC
|
||||
Statistics.TotalUsers = conn.SelectScalar("SELECT COUNT(*) FROM users") as long? ?? 0;
|
||||
}
|
||||
|
||||
private long ComputeMessageStatistics(CancellationToken token) {
|
||||
private long ComputeMessageStatistics() {
|
||||
using var conn = pool.Take();
|
||||
return conn.SelectScalar("SELECT COUNT(*) FROM messages") as long? ?? 0L;
|
||||
}
|
||||
|
@ -11,8 +11,10 @@ namespace DHT.Utils.Tasks {
|
||||
|
||||
private readonly object stateLock = new ();
|
||||
|
||||
private CancellationTokenSource? currentCancellationTokenSource;
|
||||
private Func<CancellationToken, TValue>? currentComputeFunction;
|
||||
private SoftHardCancellationToken? currentCancellationTokenSource;
|
||||
private bool wasHardCancelled = false;
|
||||
|
||||
private Func<TValue>? currentComputeFunction;
|
||||
private bool hasComputeFunctionChanged = false;
|
||||
|
||||
private AsyncValueComputer(Action<TValue> resultProcessor, TaskScheduler resultTaskScheduler, bool processOutdatedResults) {
|
||||
@ -21,12 +23,21 @@ namespace DHT.Utils.Tasks {
|
||||
this.processOutdatedResults = processOutdatedResults;
|
||||
}
|
||||
|
||||
public void Compute(Func<CancellationToken, TValue> func) {
|
||||
public void Cancel() {
|
||||
lock (stateLock) {
|
||||
wasHardCancelled = true;
|
||||
currentCancellationTokenSource?.RequestHardCancellation();
|
||||
}
|
||||
}
|
||||
|
||||
public void Compute(Func<TValue> func) {
|
||||
lock (stateLock) {
|
||||
wasHardCancelled = false;
|
||||
|
||||
if (currentComputeFunction != null) {
|
||||
currentComputeFunction = func;
|
||||
hasComputeFunctionChanged = true;
|
||||
currentCancellationTokenSource?.Cancel();
|
||||
currentCancellationTokenSource?.RequestSoftCancellation();
|
||||
}
|
||||
else {
|
||||
EnqueueComputation(func);
|
||||
@ -35,23 +46,22 @@ namespace DHT.Utils.Tasks {
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "MethodSupportsCancellation")]
|
||||
private void EnqueueComputation(Func<CancellationToken, TValue> func) {
|
||||
var cancellationTokenSource = new CancellationTokenSource();
|
||||
var cancellationToken = cancellationTokenSource.Token;
|
||||
private void EnqueueComputation(Func<TValue> func) {
|
||||
var cancellationTokenSource = new SoftHardCancellationToken();
|
||||
|
||||
currentCancellationTokenSource?.Cancel();
|
||||
currentCancellationTokenSource?.RequestSoftCancellation();
|
||||
currentCancellationTokenSource = cancellationTokenSource;
|
||||
currentComputeFunction = func;
|
||||
hasComputeFunctionChanged = false;
|
||||
|
||||
var task = Task.Run(() => func(cancellationToken));
|
||||
|
||||
var task = Task.Run(func);
|
||||
|
||||
task.ContinueWith(t => {
|
||||
if (processOutdatedResults || !cancellationToken.IsCancellationRequested) {
|
||||
if (!cancellationTokenSource.IsCancelled(processOutdatedResults)) {
|
||||
resultProcessor(t.Result);
|
||||
}
|
||||
}, CancellationToken.None, TaskContinuationOptions.NotOnFaulted, resultTaskScheduler);
|
||||
|
||||
|
||||
task.ContinueWith(_ => {
|
||||
lock (stateLock) {
|
||||
cancellationTokenSource.Dispose();
|
||||
@ -60,11 +70,12 @@ namespace DHT.Utils.Tasks {
|
||||
currentCancellationTokenSource = null;
|
||||
}
|
||||
|
||||
if (hasComputeFunctionChanged) {
|
||||
if (hasComputeFunctionChanged && !wasHardCancelled) {
|
||||
EnqueueComputation(currentComputeFunction);
|
||||
}
|
||||
else {
|
||||
currentComputeFunction = null;
|
||||
hasComputeFunctionChanged = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -72,9 +83,9 @@ namespace DHT.Utils.Tasks {
|
||||
|
||||
public sealed class Single {
|
||||
private readonly AsyncValueComputer<TValue> baseComputer;
|
||||
private readonly Func<CancellationToken, TValue> resultComputer;
|
||||
private readonly Func<TValue> resultComputer;
|
||||
|
||||
internal Single(AsyncValueComputer<TValue> baseComputer, Func<CancellationToken, TValue> resultComputer) {
|
||||
internal Single(AsyncValueComputer<TValue> baseComputer, Func<TValue> resultComputer) {
|
||||
this.baseComputer = baseComputer;
|
||||
this.resultComputer = resultComputer;
|
||||
}
|
||||
@ -107,7 +118,7 @@ namespace DHT.Utils.Tasks {
|
||||
return new AsyncValueComputer<TValue>(resultProcessor, resultTaskScheduler, processOutdatedResults);
|
||||
}
|
||||
|
||||
public Single BuildWithComputer(Func<CancellationToken, TValue> resultComputer) {
|
||||
public Single BuildWithComputer(Func<TValue> resultComputer) {
|
||||
return new Single(Build(), resultComputer);
|
||||
}
|
||||
}
|
||||
|
39
app/Utils/Tasks/SoftHardCancellationToken.cs
Normal file
39
app/Utils/Tasks/SoftHardCancellationToken.cs
Normal file
@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace DHT.Utils.Tasks {
|
||||
/// <summary>
|
||||
/// Manages a pair of cancellation tokens that follow these rules:
|
||||
/// <list type="number">
|
||||
/// <item><description>If the soft token is cancelled, the hard token remains uncancelled.</description></item>
|
||||
/// <item><description>If the hard token is cancelled, the soft token is also cancelled.</description></item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
sealed class SoftHardCancellationToken : IDisposable {
|
||||
private readonly CancellationTokenSource soft;
|
||||
private readonly CancellationTokenSource hard;
|
||||
|
||||
public SoftHardCancellationToken() {
|
||||
this.soft = new CancellationTokenSource();
|
||||
this.hard = new CancellationTokenSource();
|
||||
}
|
||||
|
||||
public bool IsCancelled(bool onlyHardCancellation) {
|
||||
return (onlyHardCancellation ? hard : soft).IsCancellationRequested;
|
||||
}
|
||||
|
||||
public void RequestSoftCancellation() {
|
||||
soft.Cancel();
|
||||
}
|
||||
|
||||
public void RequestHardCancellation() {
|
||||
soft.Cancel();
|
||||
hard.Cancel();
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
soft.Dispose();
|
||||
hard.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user