Got at least one data fetching method working; turns out, we can't use a patched LogicStack to get the data
This commit is contained in:
24
Vendor/Swan.Lite-3.1.0/Threading/AtomicBoolean.cs
vendored
Normal file
24
Vendor/Swan.Lite-3.1.0/Threading/AtomicBoolean.cs
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
namespace Swan.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// Fast, atomic boolean combining interlocked to write value and volatile to read values.
|
||||
/// </summary>
|
||||
public sealed class AtomicBoolean : AtomicTypeBase<bool>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AtomicBoolean"/> class.
|
||||
/// </summary>
|
||||
/// <param name="initialValue">if set to <c>true</c> [initial value].</param>
|
||||
public AtomicBoolean(bool initialValue = default)
|
||||
: base(initialValue ? 1 : 0)
|
||||
{
|
||||
// placeholder
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool FromLong(long backingValue) => backingValue != 0;
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override long ToLong(bool value) => value ? 1 : 0;
|
||||
}
|
||||
}
|
||||
26
Vendor/Swan.Lite-3.1.0/Threading/AtomicDateTime.cs
vendored
Normal file
26
Vendor/Swan.Lite-3.1.0/Threading/AtomicDateTime.cs
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
|
||||
namespace Swan.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines an atomic DateTime.
|
||||
/// </summary>
|
||||
public sealed class AtomicDateTime : AtomicTypeBase<DateTime>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AtomicDateTime"/> class.
|
||||
/// </summary>
|
||||
/// <param name="initialValue">The initial value.</param>
|
||||
public AtomicDateTime(DateTime initialValue)
|
||||
: base(initialValue.Ticks)
|
||||
{
|
||||
// placeholder
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override DateTime FromLong(long backingValue) => new DateTime(backingValue);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override long ToLong(DateTime value) => value.Ticks;
|
||||
}
|
||||
}
|
||||
28
Vendor/Swan.Lite-3.1.0/Threading/AtomicDouble.cs
vendored
Normal file
28
Vendor/Swan.Lite-3.1.0/Threading/AtomicDouble.cs
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
|
||||
namespace Swan.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// Fast, atomic double combining interlocked to write value and volatile to read values.
|
||||
/// </summary>
|
||||
public sealed class AtomicDouble : AtomicTypeBase<double>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AtomicDouble"/> class.
|
||||
/// </summary>
|
||||
/// <param name="initialValue">if set to <c>true</c> [initial value].</param>
|
||||
public AtomicDouble(double initialValue = default)
|
||||
: base(BitConverter.DoubleToInt64Bits(initialValue))
|
||||
{
|
||||
// placeholder
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override double FromLong(long backingValue) =>
|
||||
BitConverter.Int64BitsToDouble(backingValue);
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override long ToLong(double value) =>
|
||||
BitConverter.DoubleToInt64Bits(value);
|
||||
}
|
||||
}
|
||||
43
Vendor/Swan.Lite-3.1.0/Threading/AtomicEnum.cs
vendored
Normal file
43
Vendor/Swan.Lite-3.1.0/Threading/AtomicEnum.cs
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Swan.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines an atomic generic Enum.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of enum.</typeparam>
|
||||
public sealed class AtomicEnum<T>
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
private long _backingValue;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AtomicEnum{T}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="initialValue">The initial value.</param>
|
||||
/// <exception cref="ArgumentException">T must be an enumerated type.</exception>
|
||||
public AtomicEnum(T initialValue)
|
||||
{
|
||||
if (!Enum.IsDefined(typeof(T), initialValue))
|
||||
throw new ArgumentException("T must be an enumerated type");
|
||||
|
||||
Value = initialValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value.
|
||||
/// </summary>
|
||||
public T Value
|
||||
{
|
||||
get => (T)Enum.ToObject(typeof(T), BackingValue);
|
||||
set => BackingValue = Convert.ToInt64(value);
|
||||
}
|
||||
|
||||
private long BackingValue
|
||||
{
|
||||
get => Interlocked.Read(ref _backingValue);
|
||||
set => Interlocked.Exchange(ref _backingValue, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
28
Vendor/Swan.Lite-3.1.0/Threading/AtomicInteger.cs
vendored
Normal file
28
Vendor/Swan.Lite-3.1.0/Threading/AtomicInteger.cs
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
|
||||
namespace Swan.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an atomically readable or writable integer.
|
||||
/// </summary>
|
||||
public class AtomicInteger : AtomicTypeBase<int>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AtomicInteger"/> class.
|
||||
/// </summary>
|
||||
/// <param name="initialValue">if set to <c>true</c> [initial value].</param>
|
||||
public AtomicInteger(int initialValue = default)
|
||||
: base(Convert.ToInt64(initialValue))
|
||||
{
|
||||
// placeholder
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override int FromLong(long backingValue) =>
|
||||
Convert.ToInt32(backingValue);
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override long ToLong(int value) =>
|
||||
Convert.ToInt64(value);
|
||||
}
|
||||
}
|
||||
24
Vendor/Swan.Lite-3.1.0/Threading/AtomicLong.cs
vendored
Normal file
24
Vendor/Swan.Lite-3.1.0/Threading/AtomicLong.cs
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
namespace Swan.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// Fast, atomic long combining interlocked to write value and volatile to read values.
|
||||
/// </summary>
|
||||
public sealed class AtomicLong : AtomicTypeBase<long>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AtomicLong"/> class.
|
||||
/// </summary>
|
||||
/// <param name="initialValue">if set to <c>true</c> [initial value].</param>
|
||||
public AtomicLong(long initialValue = default)
|
||||
: base(initialValue)
|
||||
{
|
||||
// placeholder
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override long FromLong(long backingValue) => backingValue;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override long ToLong(long value) => value;
|
||||
}
|
||||
}
|
||||
26
Vendor/Swan.Lite-3.1.0/Threading/AtomicTimeSpan.cs
vendored
Normal file
26
Vendor/Swan.Lite-3.1.0/Threading/AtomicTimeSpan.cs
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
|
||||
namespace Swan.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an atomic TimeSpan type.
|
||||
/// </summary>
|
||||
public sealed class AtomicTimeSpan : AtomicTypeBase<TimeSpan>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AtomicTimeSpan" /> class.
|
||||
/// </summary>
|
||||
/// <param name="initialValue">The initial value.</param>
|
||||
public AtomicTimeSpan(TimeSpan initialValue)
|
||||
: base(initialValue.Ticks)
|
||||
{
|
||||
// placeholder
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override TimeSpan FromLong(long backingValue) => TimeSpan.FromTicks(backingValue);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override long ToLong(TimeSpan value) => value.Ticks < 0 ? 0 : value.Ticks;
|
||||
}
|
||||
}
|
||||
243
Vendor/Swan.Lite-3.1.0/Threading/AtomicTypeBase.cs
vendored
Normal file
243
Vendor/Swan.Lite-3.1.0/Threading/AtomicTypeBase.cs
vendored
Normal file
@@ -0,0 +1,243 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Swan.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a generic implementation of an Atomic (interlocked) type
|
||||
///
|
||||
/// Idea taken from Memory model and .NET operations in article:
|
||||
/// http://igoro.com/archive/volatile-keyword-in-c-memory-model-explained/.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The structure type backed by a 64-bit value.</typeparam>
|
||||
public abstract class AtomicTypeBase<T> : IComparable, IComparable<T>, IComparable<AtomicTypeBase<T>>, IEquatable<T>, IEquatable<AtomicTypeBase<T>>
|
||||
where T : struct, IComparable, IComparable<T>, IEquatable<T>
|
||||
{
|
||||
private long _backingValue;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AtomicTypeBase{T}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="initialValue">The initial value.</param>
|
||||
protected AtomicTypeBase(long initialValue)
|
||||
{
|
||||
BackingValue = initialValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value.
|
||||
/// </summary>
|
||||
public T Value
|
||||
{
|
||||
get => FromLong(BackingValue);
|
||||
set => BackingValue = ToLong(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the backing value.
|
||||
/// </summary>
|
||||
protected long BackingValue
|
||||
{
|
||||
get => Interlocked.Read(ref _backingValue);
|
||||
set => Interlocked.Exchange(ref _backingValue, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implements the operator ==.
|
||||
/// </summary>
|
||||
/// <param name="a">a.</param>
|
||||
/// <param name="b">The b.</param>
|
||||
/// <returns>
|
||||
/// The result of the operator.
|
||||
/// </returns>
|
||||
public static bool operator ==(AtomicTypeBase<T> a, T b) => a?.Equals(b) == true;
|
||||
|
||||
/// <summary>
|
||||
/// Implements the operator !=.
|
||||
/// </summary>
|
||||
/// <param name="a">a.</param>
|
||||
/// <param name="b">The b.</param>
|
||||
/// <returns>
|
||||
/// The result of the operator.
|
||||
/// </returns>
|
||||
public static bool operator !=(AtomicTypeBase<T> a, T b) => a?.Equals(b) == false;
|
||||
|
||||
/// <summary>
|
||||
/// Implements the operator >.
|
||||
/// </summary>
|
||||
/// <param name="a">a.</param>
|
||||
/// <param name="b">The b.</param>
|
||||
/// <returns>
|
||||
/// The result of the operator.
|
||||
/// </returns>
|
||||
public static bool operator >(AtomicTypeBase<T> a, T b) => a.CompareTo(b) > 0;
|
||||
|
||||
/// <summary>
|
||||
/// Implements the operator <.
|
||||
/// </summary>
|
||||
/// <param name="a">a.</param>
|
||||
/// <param name="b">The b.</param>
|
||||
/// <returns>
|
||||
/// The result of the operator.
|
||||
/// </returns>
|
||||
public static bool operator <(AtomicTypeBase<T> a, T b) => a.CompareTo(b) < 0;
|
||||
|
||||
/// <summary>
|
||||
/// Implements the operator >=.
|
||||
/// </summary>
|
||||
/// <param name="a">a.</param>
|
||||
/// <param name="b">The b.</param>
|
||||
/// <returns>
|
||||
/// The result of the operator.
|
||||
/// </returns>
|
||||
public static bool operator >=(AtomicTypeBase<T> a, T b) => a.CompareTo(b) >= 0;
|
||||
|
||||
/// <summary>
|
||||
/// Implements the operator <=.
|
||||
/// </summary>
|
||||
/// <param name="a">a.</param>
|
||||
/// <param name="b">The b.</param>
|
||||
/// <returns>
|
||||
/// The result of the operator.
|
||||
/// </returns>
|
||||
public static bool operator <=(AtomicTypeBase<T> a, T b) => a.CompareTo(b) <= 0;
|
||||
|
||||
/// <summary>
|
||||
/// Implements the operator ++.
|
||||
/// </summary>
|
||||
/// <param name="instance">The instance.</param>
|
||||
/// <returns>
|
||||
/// The result of the operator.
|
||||
/// </returns>
|
||||
public static AtomicTypeBase<T> operator ++(AtomicTypeBase<T> instance)
|
||||
{
|
||||
Interlocked.Increment(ref instance._backingValue);
|
||||
return instance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implements the operator --.
|
||||
/// </summary>
|
||||
/// <param name="instance">The instance.</param>
|
||||
/// <returns>
|
||||
/// The result of the operator.
|
||||
/// </returns>
|
||||
public static AtomicTypeBase<T> operator --(AtomicTypeBase<T> instance)
|
||||
{
|
||||
Interlocked.Decrement(ref instance._backingValue);
|
||||
return instance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implements the operator -<.
|
||||
/// </summary>
|
||||
/// <param name="instance">The instance.</param>
|
||||
/// <param name="operand">The operand.</param>
|
||||
/// <returns>
|
||||
/// The result of the operator.
|
||||
/// </returns>
|
||||
public static AtomicTypeBase<T> operator +(AtomicTypeBase<T> instance, long operand)
|
||||
{
|
||||
instance.BackingValue = instance.BackingValue + operand;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implements the operator -.
|
||||
/// </summary>
|
||||
/// <param name="instance">The instance.</param>
|
||||
/// <param name="operand">The operand.</param>
|
||||
/// <returns>
|
||||
/// The result of the operator.
|
||||
/// </returns>
|
||||
public static AtomicTypeBase<T> operator -(AtomicTypeBase<T> instance, long operand)
|
||||
{
|
||||
instance.BackingValue = instance.BackingValue - operand;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares the value to the other instance.
|
||||
/// </summary>
|
||||
/// <param name="other">The other instance.</param>
|
||||
/// <returns>0 if equal, 1 if this instance is greater, -1 if this instance is less than.</returns>
|
||||
/// <exception cref="ArgumentException">When types are incompatible.</exception>
|
||||
public int CompareTo(object other)
|
||||
{
|
||||
switch (other)
|
||||
{
|
||||
case null:
|
||||
return 1;
|
||||
case AtomicTypeBase<T> atomic:
|
||||
return BackingValue.CompareTo(atomic.BackingValue);
|
||||
case T variable:
|
||||
return Value.CompareTo(variable);
|
||||
}
|
||||
|
||||
throw new ArgumentException("Incompatible comparison types");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares the value to the other instance.
|
||||
/// </summary>
|
||||
/// <param name="other">The other instance.</param>
|
||||
/// <returns>0 if equal, 1 if this instance is greater, -1 if this instance is less than.</returns>
|
||||
public int CompareTo(T other) => Value.CompareTo(other);
|
||||
|
||||
/// <summary>
|
||||
/// Compares the value to the other instance.
|
||||
/// </summary>
|
||||
/// <param name="other">The other instance.</param>
|
||||
/// <returns>0 if equal, 1 if this instance is greater, -1 if this instance is less than.</returns>
|
||||
public int CompareTo(AtomicTypeBase<T> other) => BackingValue.CompareTo(other?.BackingValue ?? default);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified <see cref="object" />, is equal to this instance.
|
||||
/// </summary>
|
||||
/// <param name="other">The <see cref="object" /> to compare with this instance.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the specified <see cref="object" /> is equal to this instance; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public override bool Equals(object other)
|
||||
{
|
||||
switch (other)
|
||||
{
|
||||
case AtomicTypeBase<T> atomic:
|
||||
return Equals(atomic);
|
||||
case T variable:
|
||||
return Equals(variable);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a hash code for this instance.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
|
||||
/// </returns>
|
||||
public override int GetHashCode() => BackingValue.GetHashCode();
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Equals(AtomicTypeBase<T> other) =>
|
||||
BackingValue == (other?.BackingValue ?? default);
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Equals(T other) => Equals(Value, other);
|
||||
|
||||
/// <summary>
|
||||
/// Converts from a long value to the target type.
|
||||
/// </summary>
|
||||
/// <param name="backingValue">The backing value.</param>
|
||||
/// <returns>The value converted form a long value.</returns>
|
||||
protected abstract T FromLong(long backingValue);
|
||||
|
||||
/// <summary>
|
||||
/// Converts from the target type to a long value.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <returns>The value converted to a long value.</returns>
|
||||
protected abstract long ToLong(T value);
|
||||
}
|
||||
}
|
||||
68
Vendor/Swan.Lite-3.1.0/Threading/CancellationTokenOwner.cs
vendored
Normal file
68
Vendor/Swan.Lite-3.1.0/Threading/CancellationTokenOwner.cs
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Swan.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// Acts as a <see cref="CancellationTokenSource"/> but with reusable tokens.
|
||||
/// </summary>
|
||||
public sealed class CancellationTokenOwner : IDisposable
|
||||
{
|
||||
private readonly object _syncLock = new object();
|
||||
private bool _isDisposed;
|
||||
private CancellationTokenSource _tokenSource = new CancellationTokenSource();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the token of the current.
|
||||
/// </summary>
|
||||
public CancellationToken Token
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
return _isDisposed
|
||||
? CancellationToken.None
|
||||
: _tokenSource.Token;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancels the last referenced token and creates a new token source.
|
||||
/// </summary>
|
||||
public void Cancel()
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
if (_isDisposed) return;
|
||||
_tokenSource.Cancel();
|
||||
_tokenSource.Dispose();
|
||||
_tokenSource = new CancellationTokenSource();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose() => Dispose(true);
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
if (_isDisposed) return;
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
_tokenSource.Cancel();
|
||||
_tokenSource.Dispose();
|
||||
}
|
||||
|
||||
_isDisposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
232
Vendor/Swan.Lite-3.1.0/Threading/ExclusiveTimer.cs
vendored
Normal file
232
Vendor/Swan.Lite-3.1.0/Threading/ExclusiveTimer.cs
vendored
Normal file
@@ -0,0 +1,232 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Swan.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// A threading <see cref="_backingTimer"/> implementation that executes at most one cycle at a time
|
||||
/// in a <see cref="ThreadPool"/> thread. Callback execution is NOT guaranteed to be carried out
|
||||
/// on the same <see cref="ThreadPool"/> thread every time the timer fires.
|
||||
/// </summary>
|
||||
public sealed class ExclusiveTimer : IDisposable
|
||||
{
|
||||
private readonly object _syncLock = new object();
|
||||
private readonly ManualResetEventSlim _cycleDoneEvent = new ManualResetEventSlim(true);
|
||||
private readonly Timer _backingTimer;
|
||||
private readonly TimerCallback _userCallback;
|
||||
private readonly AtomicBoolean _isDisposing = new AtomicBoolean();
|
||||
private readonly AtomicBoolean _isDisposed = new AtomicBoolean();
|
||||
private int _period;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExclusiveTimer"/> class.
|
||||
/// </summary>
|
||||
/// <param name="timerCallback">The timer callback.</param>
|
||||
/// <param name="state">The state.</param>
|
||||
/// <param name="dueTime">The due time.</param>
|
||||
/// <param name="period">The period.</param>
|
||||
public ExclusiveTimer(TimerCallback timerCallback, object? state, int dueTime, int period)
|
||||
{
|
||||
_period = period;
|
||||
_userCallback = timerCallback;
|
||||
_backingTimer = new Timer(InternalCallback, state ?? this, dueTime, Timeout.Infinite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExclusiveTimer"/> class.
|
||||
/// </summary>
|
||||
/// <param name="timerCallback">The timer callback.</param>
|
||||
/// <param name="state">The state.</param>
|
||||
/// <param name="dueTime">The due time.</param>
|
||||
/// <param name="period">The period.</param>
|
||||
public ExclusiveTimer(TimerCallback timerCallback, object? state, TimeSpan dueTime, TimeSpan period)
|
||||
: this(timerCallback, state, Convert.ToInt32(dueTime.TotalMilliseconds), Convert.ToInt32(period.TotalMilliseconds))
|
||||
{
|
||||
// placeholder
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExclusiveTimer"/> class.
|
||||
/// </summary>
|
||||
/// <param name="timerCallback">The timer callback.</param>
|
||||
public ExclusiveTimer(TimerCallback timerCallback)
|
||||
: this(timerCallback, null, Timeout.Infinite, Timeout.Infinite)
|
||||
{
|
||||
// placeholder
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExclusiveTimer"/> class.
|
||||
/// </summary>
|
||||
/// <param name="timerCallback">The timer callback.</param>
|
||||
/// <param name="dueTime">The due time.</param>
|
||||
/// <param name="period">The period.</param>
|
||||
public ExclusiveTimer(Action timerCallback, int dueTime, int period)
|
||||
: this(s => { timerCallback?.Invoke(); }, null, dueTime, period)
|
||||
{
|
||||
// placeholder
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExclusiveTimer"/> class.
|
||||
/// </summary>
|
||||
/// <param name="timerCallback">The timer callback.</param>
|
||||
/// <param name="dueTime">The due time.</param>
|
||||
/// <param name="period">The period.</param>
|
||||
public ExclusiveTimer(Action timerCallback, TimeSpan dueTime, TimeSpan period)
|
||||
: this(s => { timerCallback?.Invoke(); }, null, dueTime, period)
|
||||
{
|
||||
// placeholder
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExclusiveTimer"/> class.
|
||||
/// </summary>
|
||||
/// <param name="timerCallback">The timer callback.</param>
|
||||
public ExclusiveTimer(Action timerCallback)
|
||||
: this(timerCallback, Timeout.Infinite, Timeout.Infinite)
|
||||
{
|
||||
// placeholder
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is disposing.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this instance is disposing; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool IsDisposing => _isDisposing.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is disposed.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this instance is disposed; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool IsDisposed => _isDisposed.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Waits until the time is elapsed.
|
||||
/// </summary>
|
||||
/// <param name="untilDate">The until date.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
public static void WaitUntil(DateTime untilDate, CancellationToken cancellationToken = default)
|
||||
{
|
||||
static void Callback(IWaitEvent waitEvent)
|
||||
{
|
||||
try
|
||||
{
|
||||
waitEvent.Complete();
|
||||
waitEvent.Begin();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
using var delayLock = WaitEventFactory.Create(true);
|
||||
using var timer = new ExclusiveTimer(() => Callback(delayLock), 0, 15);
|
||||
while (!cancellationToken.IsCancellationRequested && DateTime.UtcNow < untilDate)
|
||||
delayLock.Wait();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Waits the specified wait time.
|
||||
/// </summary>
|
||||
/// <param name="waitTime">The wait time.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
public static void Wait(TimeSpan waitTime, CancellationToken cancellationToken = default) =>
|
||||
WaitUntil(DateTime.UtcNow.Add(waitTime), cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Changes the start time and the interval between method invocations for the internal timer.
|
||||
/// </summary>
|
||||
/// <param name="dueTime">The due time.</param>
|
||||
/// <param name="period">The period.</param>
|
||||
public void Change(int dueTime, int period)
|
||||
{
|
||||
_period = period;
|
||||
|
||||
_backingTimer.Change(dueTime, Timeout.Infinite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the start time and the interval between method invocations for the internal timer.
|
||||
/// </summary>
|
||||
/// <param name="dueTime">The due time.</param>
|
||||
/// <param name="period">The period.</param>
|
||||
public void Change(TimeSpan dueTime, TimeSpan period)
|
||||
=> Change(Convert.ToInt32(dueTime.TotalMilliseconds), Convert.ToInt32(period.TotalMilliseconds));
|
||||
|
||||
/// <summary>
|
||||
/// Changes the interval between method invocations for the internal timer.
|
||||
/// </summary>
|
||||
/// <param name="period">The period.</param>
|
||||
public void Resume(int period) => Change(0, period);
|
||||
|
||||
/// <summary>
|
||||
/// Changes the interval between method invocations for the internal timer.
|
||||
/// </summary>
|
||||
/// <param name="period">The period.</param>
|
||||
public void Resume(TimeSpan period) => Change(TimeSpan.Zero, period);
|
||||
|
||||
/// <summary>
|
||||
/// Pauses this instance.
|
||||
/// </summary>
|
||||
public void Pause() => Change(Timeout.Infinite, Timeout.Infinite);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
if (_isDisposed == true || _isDisposing == true)
|
||||
return;
|
||||
|
||||
_isDisposing.Value = true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_cycleDoneEvent.Wait();
|
||||
_cycleDoneEvent.Dispose();
|
||||
Pause();
|
||||
_backingTimer.Dispose();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isDisposed.Value = true;
|
||||
_isDisposing.Value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logic that runs every time the timer hits the due time.
|
||||
/// </summary>
|
||||
/// <param name="state">The state.</param>
|
||||
private void InternalCallback(object state)
|
||||
{
|
||||
lock (_syncLock)
|
||||
{
|
||||
if (IsDisposed || IsDisposing)
|
||||
return;
|
||||
}
|
||||
|
||||
if (_cycleDoneEvent.IsSet == false)
|
||||
return;
|
||||
|
||||
_cycleDoneEvent.Reset();
|
||||
|
||||
try
|
||||
{
|
||||
_userCallback(state);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_cycleDoneEvent?.Set();
|
||||
_backingTimer?.Change(_period, Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
24
Vendor/Swan.Lite-3.1.0/Threading/ISyncLocker.cs
vendored
Normal file
24
Vendor/Swan.Lite-3.1.0/Threading/ISyncLocker.cs
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
|
||||
namespace Swan.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a generic interface for synchronized locking mechanisms.
|
||||
/// </summary>
|
||||
public interface ISyncLocker : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Acquires a writer lock.
|
||||
/// The lock is released when the returned locking object is disposed.
|
||||
/// </summary>
|
||||
/// <returns>A disposable locking object.</returns>
|
||||
IDisposable AcquireWriterLock();
|
||||
|
||||
/// <summary>
|
||||
/// Acquires a reader lock.
|
||||
/// The lock is released when the returned locking object is disposed.
|
||||
/// </summary>
|
||||
/// <returns>A disposable locking object.</returns>
|
||||
IDisposable AcquireReaderLock();
|
||||
}
|
||||
}
|
||||
57
Vendor/Swan.Lite-3.1.0/Threading/IWaitEvent.cs
vendored
Normal file
57
Vendor/Swan.Lite-3.1.0/Threading/IWaitEvent.cs
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
|
||||
namespace Swan.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a generalized API for ManualResetEvent and ManualResetEventSlim.
|
||||
/// </summary>
|
||||
/// <seealso cref="IDisposable" />
|
||||
public interface IWaitEvent : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the event is in the completed state.
|
||||
/// </summary>
|
||||
bool IsCompleted { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the Begin method has been called.
|
||||
/// It returns false after the Complete method is called.
|
||||
/// </summary>
|
||||
bool IsInProgress { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the underlying handle is not closed and it is still valid.
|
||||
/// </summary>
|
||||
bool IsValid { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is disposed.
|
||||
/// </summary>
|
||||
bool IsDisposed { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Enters the state in which waiters need to wait.
|
||||
/// All future waiters will block when they call the Wait method.
|
||||
/// </summary>
|
||||
void Begin();
|
||||
|
||||
/// <summary>
|
||||
/// Leaves the state in which waiters need to wait.
|
||||
/// All current waiters will continue.
|
||||
/// </summary>
|
||||
void Complete();
|
||||
|
||||
/// <summary>
|
||||
/// Waits for the event to be completed.
|
||||
/// </summary>
|
||||
void Wait();
|
||||
|
||||
/// <summary>
|
||||
/// Waits for the event to be completed.
|
||||
/// Returns <c>true</c> when there was no timeout. False if the timeout was reached.
|
||||
/// </summary>
|
||||
/// <param name="timeout">The maximum amount of time to wait for.</param>
|
||||
/// <returns><c>true</c> when there was no timeout. <c>false</c> if the timeout was reached.</returns>
|
||||
bool Wait(TimeSpan timeout);
|
||||
}
|
||||
}
|
||||
69
Vendor/Swan.Lite-3.1.0/Threading/IWorker.cs
vendored
Normal file
69
Vendor/Swan.Lite-3.1.0/Threading/IWorker.cs
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Swan.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a standard API to control background application workers.
|
||||
/// </summary>
|
||||
public interface IWorker
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the current state of the worker.
|
||||
/// </summary>
|
||||
WorkerState WorkerState { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is disposed.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this instance is disposed; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
bool IsDisposed { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is currently being disposed.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this instance is disposing; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
bool IsDisposing { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the time interval used to execute cycles.
|
||||
/// </summary>
|
||||
TimeSpan Period { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name identifier of this worker.
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Starts execution of worker cycles.
|
||||
/// </summary>
|
||||
/// <returns>The awaitable task.</returns>
|
||||
Task<WorkerState> StartAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Pauses execution of worker cycles.
|
||||
/// </summary>
|
||||
/// <returns>The awaitable task.</returns>
|
||||
Task<WorkerState> PauseAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Resumes execution of worker cycles.
|
||||
/// </summary>
|
||||
/// <returns>The awaitable task.</returns>
|
||||
Task<WorkerState> ResumeAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Permanently stops execution of worker cycles.
|
||||
/// An interrupt is always sent to the worker. If you wish to stop
|
||||
/// the worker without interrupting then call the <see cref="PauseAsync"/>
|
||||
/// method, await it, and finally call the <see cref="StopAsync"/> method.
|
||||
/// </summary>
|
||||
/// <returns>The awaitable task.</returns>
|
||||
Task<WorkerState> StopAsync();
|
||||
}
|
||||
}
|
||||
21
Vendor/Swan.Lite-3.1.0/Threading/IWorkerDelayProvider.cs
vendored
Normal file
21
Vendor/Swan.Lite-3.1.0/Threading/IWorkerDelayProvider.cs
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Swan.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// An interface for a worker cycle delay provider.
|
||||
/// </summary>
|
||||
public interface IWorkerDelayProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Suspends execution queues a new cycle for execution. The delay is given in
|
||||
/// milliseconds. When overridden in a derived class the wait handle will be set
|
||||
/// whenever an interrupt is received.
|
||||
/// </summary>
|
||||
/// <param name="wantedDelay">The remaining delay to wait for in the cycle.</param>
|
||||
/// <param name="delayTask">Contains a reference to a task with the scheduled period delay.</param>
|
||||
/// <param name="token">The cancellation token to cancel waiting.</param>
|
||||
void ExecuteCycleDelay(int wantedDelay, Task delayTask, CancellationToken token);
|
||||
}
|
||||
}
|
||||
101
Vendor/Swan.Lite-3.1.0/Threading/PeriodicTask.cs
vendored
Normal file
101
Vendor/Swan.Lite-3.1.0/Threading/PeriodicTask.cs
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Swan.Logging;
|
||||
|
||||
namespace Swan.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// Schedule an action to be periodically executed on the thread pool.
|
||||
/// </summary>
|
||||
public sealed class PeriodicTask : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>The minimum interval between action invocations.</para>
|
||||
/// <para>The value of this field is equal to 100 milliseconds.</para>
|
||||
/// </summary>
|
||||
public static readonly TimeSpan MinInterval = TimeSpan.FromMilliseconds(100);
|
||||
|
||||
private readonly Func<CancellationToken, Task> _action;
|
||||
private readonly CancellationTokenSource _cancellationTokenSource;
|
||||
|
||||
private TimeSpan _interval;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PeriodicTask"/> class.
|
||||
/// </summary>
|
||||
/// <param name="interval">The interval between invocations of <paramref name="action"/>.</param>
|
||||
/// <param name="action">The callback to invoke periodically.</param>
|
||||
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel operations.</param>
|
||||
public PeriodicTask(TimeSpan interval, Func<CancellationToken, Task> action, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_action = action ?? throw new ArgumentNullException(nameof(action));
|
||||
_cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
||||
_interval = ValidateInterval(interval);
|
||||
|
||||
Task.Run(ActionLoop);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finalizes an instance of the <see cref="PeriodicTask"/> class.
|
||||
/// </summary>
|
||||
~PeriodicTask()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>Gets or sets the interval between periodic action invocations.</para>
|
||||
/// <para>Changes to this property take effect after next action invocation.</para>
|
||||
/// </summary>
|
||||
/// <seealso cref="MinInterval"/>
|
||||
public TimeSpan Interval
|
||||
{
|
||||
get => _interval;
|
||||
set => _interval = ValidateInterval(value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_cancellationTokenSource.Cancel();
|
||||
_cancellationTokenSource.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private static TimeSpan ValidateInterval(TimeSpan value)
|
||||
=> value < MinInterval ? MinInterval : value;
|
||||
|
||||
private async Task ActionLoop()
|
||||
{
|
||||
for (; ; )
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(Interval, _cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
await _action(_cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
}
|
||||
catch (OperationCanceledException) when (_cancellationTokenSource.IsCancellationRequested)
|
||||
{
|
||||
break;
|
||||
}
|
||||
catch (TaskCanceledException) when (_cancellationTokenSource.IsCancellationRequested)
|
||||
{
|
||||
break;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ex.Log(nameof(PeriodicTask));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
187
Vendor/Swan.Lite-3.1.0/Threading/RunnerBase.cs
vendored
Normal file
187
Vendor/Swan.Lite-3.1.0/Threading/RunnerBase.cs
vendored
Normal file
@@ -0,0 +1,187 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Swan.Configuration;
|
||||
using Swan.Logging;
|
||||
|
||||
namespace Swan.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an background worker abstraction with a life cycle and running at a independent thread.
|
||||
/// </summary>
|
||||
public abstract class RunnerBase : ConfiguredObject, IDisposable
|
||||
{
|
||||
private Thread? _worker;
|
||||
private CancellationTokenSource? _cancelTokenSource;
|
||||
private ManualResetEvent? _workFinished;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RunnerBase"/> class.
|
||||
/// </summary>
|
||||
/// <param name="isEnabled">if set to <c>true</c> [is enabled].</param>
|
||||
protected RunnerBase(bool isEnabled)
|
||||
{
|
||||
Name = GetType().Name;
|
||||
IsEnabled = isEnabled;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the error messages.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The error messages.
|
||||
/// </value>
|
||||
public List<string> ErrorMessages { get; } = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The name.
|
||||
/// </value>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is running.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this instance is running; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool IsRunning { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this instance is enabled.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this instance is enabled; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool IsEnabled { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Starts this instance.
|
||||
/// </summary>
|
||||
public virtual void Start()
|
||||
{
|
||||
if (IsEnabled == false)
|
||||
return;
|
||||
|
||||
"Start Requested".Debug(Name);
|
||||
_cancelTokenSource = new CancellationTokenSource();
|
||||
_workFinished = new ManualResetEvent(false);
|
||||
|
||||
_worker = new Thread(() =>
|
||||
{
|
||||
_workFinished.Reset();
|
||||
IsRunning = true;
|
||||
try
|
||||
{
|
||||
Setup();
|
||||
DoBackgroundWork(_cancelTokenSource.Token);
|
||||
}
|
||||
catch (ThreadAbortException)
|
||||
{
|
||||
$"{nameof(ThreadAbortException)} caught.".Warn(Name);
|
||||
}
|
||||
#pragma warning disable CA1031 // Do not catch general exception types
|
||||
catch (Exception ex)
|
||||
#pragma warning restore CA1031 // Do not catch general exception types
|
||||
{
|
||||
$"{ex.GetType()}: {ex.Message}\r\n{ex.StackTrace}".Error(Name);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Cleanup();
|
||||
_workFinished?.Set();
|
||||
IsRunning = false;
|
||||
"Stopped Completely".Debug(Name);
|
||||
}
|
||||
})
|
||||
{
|
||||
IsBackground = true,
|
||||
Name = $"{Name}Thread",
|
||||
};
|
||||
|
||||
_worker.Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops this instance.
|
||||
/// </summary>
|
||||
public virtual void Stop()
|
||||
{
|
||||
if (IsEnabled == false || IsRunning == false)
|
||||
return;
|
||||
|
||||
"Stop Requested".Debug(Name);
|
||||
_cancelTokenSource?.Cancel();
|
||||
var waitRetries = 5;
|
||||
while (waitRetries >= 1)
|
||||
{
|
||||
if (_workFinished?.WaitOne(250) ?? true)
|
||||
{
|
||||
waitRetries = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
waitRetries--;
|
||||
}
|
||||
|
||||
if (waitRetries < 0)
|
||||
{
|
||||
"Workbench stopped gracefully".Debug(Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
"Did not respond to stop request. Aborting thread and waiting . . .".Warn(Name);
|
||||
_worker?.Abort();
|
||||
|
||||
if (_workFinished?.WaitOne(5000) == false)
|
||||
"Waited and no response. Worker might have been left in an inconsistent state.".Error(Name);
|
||||
else
|
||||
"Waited for worker and it finally responded (OK).".Debug(Name);
|
||||
}
|
||||
|
||||
_workFinished?.Dispose();
|
||||
_workFinished = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
_cancelTokenSource?.Dispose();
|
||||
_workFinished?.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Setups this instance.
|
||||
/// </summary>
|
||||
protected void Setup()
|
||||
{
|
||||
EnsureConfigurationNotLocked();
|
||||
OnSetup();
|
||||
LockConfiguration();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleanups this instance.
|
||||
/// </summary>
|
||||
protected virtual void Cleanup()
|
||||
{
|
||||
// empty
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when [setup].
|
||||
/// </summary>
|
||||
protected virtual void OnSetup()
|
||||
{
|
||||
// empty
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does the background work.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The ct.</param>
|
||||
protected abstract void DoBackgroundWork(CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
185
Vendor/Swan.Lite-3.1.0/Threading/SyncLockerFactory.cs
vendored
Normal file
185
Vendor/Swan.Lite-3.1.0/Threading/SyncLockerFactory.cs
vendored
Normal file
@@ -0,0 +1,185 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Swan.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides factory methods to create synchronized reader-writer locks
|
||||
/// that support a generalized locking and releasing api and syntax.
|
||||
/// </summary>
|
||||
public static class SyncLockerFactory
|
||||
{
|
||||
#region Enums and Interfaces
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates the locking operations.
|
||||
/// </summary>
|
||||
private enum LockHolderType
|
||||
{
|
||||
Read,
|
||||
Write,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines methods for releasing locks.
|
||||
/// </summary>
|
||||
private interface ISyncReleasable
|
||||
{
|
||||
/// <summary>
|
||||
/// Releases the writer lock.
|
||||
/// </summary>
|
||||
void ReleaseWriterLock();
|
||||
|
||||
/// <summary>
|
||||
/// Releases the reader lock.
|
||||
/// </summary>
|
||||
void ReleaseReaderLock();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Factory Methods
|
||||
|
||||
/// <summary>
|
||||
/// Creates a reader-writer lock backed by a standard ReaderWriterLock.
|
||||
/// </summary>
|
||||
/// <returns>The synchronized locker.</returns>
|
||||
public static ISyncLocker Create() => new SyncLocker();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a reader-writer lock backed by a ReaderWriterLockSlim.
|
||||
/// </summary>
|
||||
/// <returns>The synchronized locker.</returns>
|
||||
public static ISyncLocker CreateSlim() => new SyncLockerSlim();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a reader-writer lock.
|
||||
/// </summary>
|
||||
/// <param name="useSlim">if set to <c>true</c> it uses the Slim version of a reader-writer lock.</param>
|
||||
/// <returns>The Sync Locker.</returns>
|
||||
public static ISyncLocker Create(bool useSlim) => useSlim ? CreateSlim() : Create();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Classes
|
||||
|
||||
/// <summary>
|
||||
/// The lock releaser. Calling the dispose method releases the lock entered by the parent SyncLocker.
|
||||
/// </summary>
|
||||
/// <seealso cref="System.IDisposable" />
|
||||
private sealed class SyncLockReleaser : IDisposable
|
||||
{
|
||||
private readonly ISyncReleasable _parent;
|
||||
private readonly LockHolderType _operation;
|
||||
|
||||
private bool _isDisposed;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SyncLockReleaser"/> class.
|
||||
/// </summary>
|
||||
/// <param name="parent">The parent.</param>
|
||||
/// <param name="operation">The operation.</param>
|
||||
public SyncLockReleaser(ISyncReleasable parent, LockHolderType operation)
|
||||
{
|
||||
_parent = parent;
|
||||
_operation = operation;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDisposed) return;
|
||||
_isDisposed = true;
|
||||
|
||||
if (_operation == LockHolderType.Read)
|
||||
_parent.ReleaseReaderLock();
|
||||
else
|
||||
_parent.ReleaseWriterLock();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Sync Locker backed by a ReaderWriterLock.
|
||||
/// </summary>
|
||||
/// <seealso cref="ISyncLocker" />
|
||||
/// <seealso cref="ISyncReleasable" />
|
||||
private sealed class SyncLocker : ISyncLocker, ISyncReleasable
|
||||
{
|
||||
private bool _isDisposed;
|
||||
private ReaderWriterLock? _locker = new ReaderWriterLock();
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDisposable AcquireReaderLock()
|
||||
{
|
||||
_locker?.AcquireReaderLock(Timeout.Infinite);
|
||||
return new SyncLockReleaser(this, LockHolderType.Read);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDisposable AcquireWriterLock()
|
||||
{
|
||||
_locker?.AcquireWriterLock(Timeout.Infinite);
|
||||
return new SyncLockReleaser(this, LockHolderType.Write);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ReleaseWriterLock() => _locker?.ReleaseWriterLock();
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ReleaseReaderLock() => _locker?.ReleaseReaderLock();
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDisposed) return;
|
||||
_isDisposed = true;
|
||||
_locker?.ReleaseLock();
|
||||
_locker = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Sync Locker backed by ReaderWriterLockSlim.
|
||||
/// </summary>
|
||||
/// <seealso cref="ISyncLocker" />
|
||||
/// <seealso cref="ISyncReleasable" />
|
||||
private sealed class SyncLockerSlim : ISyncLocker, ISyncReleasable
|
||||
{
|
||||
private bool _isDisposed;
|
||||
|
||||
private ReaderWriterLockSlim _locker
|
||||
= new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDisposable AcquireReaderLock()
|
||||
{
|
||||
_locker?.EnterReadLock();
|
||||
return new SyncLockReleaser(this, LockHolderType.Read);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDisposable AcquireWriterLock()
|
||||
{
|
||||
_locker?.EnterWriteLock();
|
||||
return new SyncLockReleaser(this, LockHolderType.Write);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ReleaseWriterLock() => _locker?.ExitWriteLock();
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ReleaseReaderLock() => _locker?.ExitReadLock();
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDisposed) return;
|
||||
_isDisposed = true;
|
||||
_locker?.Dispose();
|
||||
_locker = null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
204
Vendor/Swan.Lite-3.1.0/Threading/WaitEventFactory.cs
vendored
Normal file
204
Vendor/Swan.Lite-3.1.0/Threading/WaitEventFactory.cs
vendored
Normal file
@@ -0,0 +1,204 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Swan.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a Manual Reset Event factory with a unified API.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// The following example shows how to use the WaitEventFactory class.
|
||||
/// <code>
|
||||
/// using Swan.Threading;
|
||||
///
|
||||
/// public class Example
|
||||
/// {
|
||||
/// // create a WaitEvent using the slim version
|
||||
/// private static readonly IWaitEvent waitEvent = WaitEventFactory.CreateSlim(false);
|
||||
///
|
||||
/// public static void Main()
|
||||
/// {
|
||||
/// Task.Factory.StartNew(() =>
|
||||
/// {
|
||||
/// DoWork(1);
|
||||
/// });
|
||||
///
|
||||
/// Task.Factory.StartNew(() =>
|
||||
/// {
|
||||
/// DoWork(2);
|
||||
/// });
|
||||
///
|
||||
/// // send first signal
|
||||
/// waitEvent.Complete();
|
||||
/// waitEvent.Begin();
|
||||
///
|
||||
/// Thread.Sleep(TimeSpan.FromSeconds(2));
|
||||
///
|
||||
/// // send second signal
|
||||
/// waitEvent.Complete();
|
||||
///
|
||||
/// Terminal.Readline();
|
||||
/// }
|
||||
///
|
||||
/// public static void DoWork(int taskNumber)
|
||||
/// {
|
||||
/// $"Data retrieved:{taskNumber}".WriteLine();
|
||||
/// waitEvent.Wait();
|
||||
///
|
||||
/// Thread.Sleep(TimeSpan.FromSeconds(2));
|
||||
/// $"All finished up {taskNumber}".WriteLine();
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static class WaitEventFactory
|
||||
{
|
||||
#region Factory Methods
|
||||
|
||||
/// <summary>
|
||||
/// Creates a Wait Event backed by a standard ManualResetEvent.
|
||||
/// </summary>
|
||||
/// <param name="isCompleted">if initially set to completed. Generally true.</param>
|
||||
/// <returns>The Wait Event.</returns>
|
||||
public static IWaitEvent Create(bool isCompleted) => new WaitEvent(isCompleted);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a Wait Event backed by a ManualResetEventSlim.
|
||||
/// </summary>
|
||||
/// <param name="isCompleted">if initially set to completed. Generally true.</param>
|
||||
/// <returns>The Wait Event.</returns>
|
||||
public static IWaitEvent CreateSlim(bool isCompleted) => new WaitEventSlim(isCompleted);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a Wait Event backed by a ManualResetEventSlim.
|
||||
/// </summary>
|
||||
/// <param name="isCompleted">if initially set to completed. Generally true.</param>
|
||||
/// <param name="useSlim">if set to <c>true</c> creates a slim version of the wait event.</param>
|
||||
/// <returns>The Wait Event.</returns>
|
||||
public static IWaitEvent Create(bool isCompleted, bool useSlim) => useSlim ? CreateSlim(isCompleted) : Create(isCompleted);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Backing Classes
|
||||
|
||||
/// <summary>
|
||||
/// Defines a WaitEvent backed by a ManualResetEvent.
|
||||
/// </summary>
|
||||
private class WaitEvent : IWaitEvent
|
||||
{
|
||||
private ManualResetEvent? _event;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WaitEvent"/> class.
|
||||
/// </summary>
|
||||
/// <param name="isCompleted">if set to <c>true</c> [is completed].</param>
|
||||
public WaitEvent(bool isCompleted)
|
||||
{
|
||||
_event = new ManualResetEvent(isCompleted);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsDisposed { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsValid
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsDisposed || _event == null)
|
||||
return false;
|
||||
|
||||
if (_event?.SafeWaitHandle?.IsClosed ?? true)
|
||||
return false;
|
||||
|
||||
return !(_event?.SafeWaitHandle?.IsInvalid ?? true);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsCompleted => IsValid == false || (_event?.WaitOne(0) ?? true);
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsInProgress => !IsCompleted;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Begin() => _event?.Reset();
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Complete() => _event?.Set();
|
||||
|
||||
/// <inheritdoc />
|
||||
void IDisposable.Dispose()
|
||||
{
|
||||
if (IsDisposed) return;
|
||||
IsDisposed = true;
|
||||
|
||||
_event?.Set();
|
||||
_event?.Dispose();
|
||||
_event = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Wait() => _event?.WaitOne();
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Wait(TimeSpan timeout) => _event?.WaitOne(timeout) ?? true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines a WaitEvent backed by a ManualResetEventSlim.
|
||||
/// </summary>
|
||||
private class WaitEventSlim : IWaitEvent
|
||||
{
|
||||
private ManualResetEventSlim? _event;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WaitEventSlim"/> class.
|
||||
/// </summary>
|
||||
/// <param name="isCompleted">if set to <c>true</c> [is completed].</param>
|
||||
public WaitEventSlim(bool isCompleted)
|
||||
{
|
||||
_event = new ManualResetEventSlim(isCompleted);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsDisposed { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsValid =>
|
||||
!IsDisposed && _event?.WaitHandle?.SafeWaitHandle != null &&
|
||||
(!_event.WaitHandle.SafeWaitHandle.IsClosed && !_event.WaitHandle.SafeWaitHandle.IsInvalid);
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsCompleted => IsValid == false || _event?.IsSet == true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsInProgress => !IsCompleted;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Begin() => _event?.Reset();
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Complete() => _event?.Set();
|
||||
|
||||
/// <inheritdoc />
|
||||
void IDisposable.Dispose()
|
||||
{
|
||||
if (IsDisposed) return;
|
||||
IsDisposed = true;
|
||||
|
||||
_event?.Set();
|
||||
_event?.Dispose();
|
||||
_event = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Wait() => _event?.Wait();
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Wait(TimeSpan timeout) => _event?.Wait(timeout) ?? true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
33
Vendor/Swan.Lite-3.1.0/Threading/WorkerState.cs
vendored
Normal file
33
Vendor/Swan.Lite-3.1.0/Threading/WorkerState.cs
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
namespace Swan.Threading
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerates the different states in which a worker can be.
|
||||
/// </summary>
|
||||
public enum WorkerState
|
||||
{
|
||||
/// <summary>
|
||||
/// The worker has been created and it is ready to start.
|
||||
/// </summary>
|
||||
Created,
|
||||
|
||||
/// <summary>
|
||||
/// The worker is running it cycle logic.
|
||||
/// </summary>
|
||||
Running,
|
||||
|
||||
/// <summary>
|
||||
/// The worker is running its delay logic.
|
||||
/// </summary>
|
||||
Waiting,
|
||||
|
||||
/// <summary>
|
||||
/// The worker is in the paused or suspended state.
|
||||
/// </summary>
|
||||
Paused,
|
||||
|
||||
/// <summary>
|
||||
/// The worker is stopped and ready for disposal.
|
||||
/// </summary>
|
||||
Stopped,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user