coding styles nullable

This commit is contained in:
2019-12-08 19:54:52 +01:00
parent c78348324a
commit aa9fcd4a36
109 changed files with 12952 additions and 14462 deletions
+19 -21
View File
@@ -1,24 +1,22 @@
namespace Swan.Threading
{
using System;
namespace Swan.Threading {
/// <summary>
/// Fast, atomic boolean combining interlocked to write value and volatile to read values.
/// </summary>
public sealed class AtomicBoolean : AtomicTypeBase<Boolean> {
/// <summary>
/// Fast, atomic boolean combining interlocked to write value and volatile to read values.
/// Initializes a new instance of the <see cref="AtomicBoolean"/> class.
/// </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;
}
/// <param name="initialValue">if set to <c>true</c> [initial value].</param>
public AtomicBoolean(Boolean initialValue = default) : base(initialValue ? 1 : 0) {
// placeholder
}
/// <inheritdoc/>
protected override Boolean FromLong(Int64 backingValue) => backingValue != 0;
/// <inheritdoc/>
protected override Int64 ToLong(Boolean value) => value ? 1 : 0;
}
}
+17 -21
View File
@@ -1,26 +1,22 @@
using System;
namespace Swan.Threading
{
namespace Swan.Threading {
/// <summary>
/// Defines an atomic DateTime.
/// </summary>
public sealed class AtomicDateTime : AtomicTypeBase<DateTime> {
/// <summary>
/// Defines an atomic DateTime.
/// Initializes a new instance of the <see cref="AtomicDateTime"/> class.
/// </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;
}
/// <param name="initialValue">The initial value.</param>
public AtomicDateTime(DateTime initialValue) : base(initialValue.Ticks) {
// placeholder
}
/// <inheritdoc />
protected override DateTime FromLong(Int64 backingValue) => new DateTime(backingValue);
/// <inheritdoc />
protected override Int64 ToLong(DateTime value) => value.Ticks;
}
}
+17 -23
View File
@@ -1,28 +1,22 @@
using System;
namespace Swan.Threading
{
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>
/// Fast, atomic double combining interlocked to write value and volatile to read values.
/// Initializes a new instance of the <see cref="AtomicDouble"/> class.
/// </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);
}
/// <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(Int64 backingValue) => BitConverter.Int64BitsToDouble(backingValue);
/// <inheritdoc/>
protected override Int64 ToLong(Double value) => BitConverter.DoubleToInt64Bits(value);
}
}
+32 -37
View File
@@ -1,43 +1,38 @@
using System;
using System.Threading;
namespace Swan.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 Int64 _backingValue;
/// <summary>
/// Defines an atomic generic Enum.
/// Initializes a new instance of the <see cref="AtomicEnum{T}"/> class.
/// </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);
}
}
/// <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");
}
this.Value = initialValue;
}
/// <summary>
/// Gets or sets the value.
/// </summary>
public T Value {
get => (T)Enum.ToObject(typeof(T), this.BackingValue);
set => this.BackingValue = Convert.ToInt64(value);
}
private Int64 BackingValue {
get => Interlocked.Read(ref this._backingValue);
set => Interlocked.Exchange(ref this._backingValue, value);
}
}
}
+17 -23
View File
@@ -1,28 +1,22 @@
using System;
namespace Swan.Threading
{
namespace Swan.Threading {
/// <summary>
/// Represents an atomically readable or writable integer.
/// </summary>
public class AtomicInteger : AtomicTypeBase<Int32> {
/// <summary>
/// Represents an atomically readable or writable integer.
/// Initializes a new instance of the <see cref="AtomicInteger"/> class.
/// </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);
}
/// <param name="initialValue">if set to <c>true</c> [initial value].</param>
public AtomicInteger(Int32 initialValue = default) : base(Convert.ToInt64(initialValue)) {
// placeholder
}
/// <inheritdoc/>
protected override Int32 FromLong(Int64 backingValue) => Convert.ToInt32(backingValue);
/// <inheritdoc/>
protected override Int64 ToLong(Int32 value) => Convert.ToInt64(value);
}
}
+19 -21
View File
@@ -1,24 +1,22 @@
namespace Swan.Threading
{
using System;
namespace Swan.Threading {
/// <summary>
/// Fast, atomic long combining interlocked to write value and volatile to read values.
/// </summary>
public sealed class AtomicLong : AtomicTypeBase<Int64> {
/// <summary>
/// Fast, atomic long combining interlocked to write value and volatile to read values.
/// Initializes a new instance of the <see cref="AtomicLong"/> class.
/// </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;
}
/// <param name="initialValue">if set to <c>true</c> [initial value].</param>
public AtomicLong(Int64 initialValue = default) : base(initialValue) {
// placeholder
}
/// <inheritdoc />
protected override Int64 FromLong(Int64 backingValue) => backingValue;
/// <inheritdoc />
protected override Int64 ToLong(Int64 value) => value;
}
}
+17 -21
View File
@@ -1,26 +1,22 @@
using System;
namespace Swan.Threading
{
namespace Swan.Threading {
/// <summary>
/// Represents an atomic TimeSpan type.
/// </summary>
public sealed class AtomicTimeSpan : AtomicTypeBase<TimeSpan> {
/// <summary>
/// Represents an atomic TimeSpan type.
/// Initializes a new instance of the <see cref="AtomicTimeSpan" /> class.
/// </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;
}
/// <param name="initialValue">The initial value.</param>
public AtomicTimeSpan(TimeSpan initialValue) : base(initialValue.Ticks) {
// placeholder
}
/// <inheritdoc />
protected override TimeSpan FromLong(Int64 backingValue) => TimeSpan.FromTicks(backingValue);
/// <inheritdoc />
protected override Int64 ToLong(TimeSpan value) => value.Ticks < 0 ? 0 : value.Ticks;
}
}
+213 -237
View File
@@ -1,243 +1,219 @@
using System;
using System.Threading;
namespace Swan.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 Int64 _backingValue;
/// <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/.
/// Initializes a new instance of the <see cref="AtomicTypeBase{T}"/> class.
/// </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 &gt;.
/// </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 &lt;.
/// </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 &gt;=.
/// </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 &lt;=.
/// </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 -&lt;.
/// </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);
}
/// <param name="initialValue">The initial value.</param>
protected AtomicTypeBase(Int64 initialValue) => this.BackingValue = initialValue;
/// <summary>
/// Gets or sets the value.
/// </summary>
public T Value {
get => this.FromLong(this.BackingValue);
set => this.BackingValue = this.ToLong(value);
}
/// <summary>
/// Gets or sets the backing value.
/// </summary>
protected Int64 BackingValue {
get => Interlocked.Read(ref this._backingValue);
set => Interlocked.Exchange(ref this._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 Boolean 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 Boolean operator !=(AtomicTypeBase<T> a, T b) => a?.Equals(b) == false;
/// <summary>
/// Implements the operator &gt;.
/// </summary>
/// <param name="a">a.</param>
/// <param name="b">The b.</param>
/// <returns>
/// The result of the operator.
/// </returns>
public static Boolean operator >(AtomicTypeBase<T> a, T b) => a.CompareTo(b) > 0;
/// <summary>
/// Implements the operator &lt;.
/// </summary>
/// <param name="a">a.</param>
/// <param name="b">The b.</param>
/// <returns>
/// The result of the operator.
/// </returns>
public static Boolean operator <(AtomicTypeBase<T> a, T b) => a.CompareTo(b) < 0;
/// <summary>
/// Implements the operator &gt;=.
/// </summary>
/// <param name="a">a.</param>
/// <param name="b">The b.</param>
/// <returns>
/// The result of the operator.
/// </returns>
public static Boolean operator >=(AtomicTypeBase<T> a, T b) => a.CompareTo(b) >= 0;
/// <summary>
/// Implements the operator &lt;=.
/// </summary>
/// <param name="a">a.</param>
/// <param name="b">The b.</param>
/// <returns>
/// The result of the operator.
/// </returns>
public static Boolean 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 -&lt;.
/// </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, Int64 operand) {
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, Int64 operand) {
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 Int32 CompareTo(Object other) => other switch
{
null => 1,
AtomicTypeBase<T> atomic => this.BackingValue.CompareTo(atomic.BackingValue),
T variable => this.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 Int32 CompareTo(T other) => this.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 Int32 CompareTo(AtomicTypeBase<T> other) => this.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 Boolean Equals(Object other) => other switch
{
AtomicTypeBase<T> atomic => this.Equals(atomic),
T variable => this.Equals(variable),
_ => 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 Int32 GetHashCode() => this.BackingValue.GetHashCode();
/// <inheritdoc />
public Boolean Equals(AtomicTypeBase<T> other) => this.BackingValue == (other?.BackingValue ?? default);
/// <inheritdoc />
public Boolean Equals(T other) => Equals(this.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(Int64 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 Int64 ToLong(T value);
}
}
+55 -62
View File
@@ -1,68 +1,61 @@
using System;
using System.Threading;
namespace Swan.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 Boolean _isDisposed;
private CancellationTokenSource _tokenSource = new CancellationTokenSource();
/// <summary>
/// Acts as a <see cref="CancellationTokenSource"/> but with reusable tokens.
/// Gets the token of the current.
/// </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;
}
}
}
public CancellationToken Token {
get {
lock(this._syncLock) {
return this._isDisposed ? CancellationToken.None : this._tokenSource.Token;
}
}
}
/// <summary>
/// Cancels the last referenced token and creates a new token source.
/// </summary>
public void Cancel() {
lock(this._syncLock) {
if(this._isDisposed) {
return;
}
this._tokenSource.Cancel();
this._tokenSource.Dispose();
this._tokenSource = new CancellationTokenSource();
}
}
/// <inheritdoc />
public void Dispose() => this.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(Boolean disposing) {
lock(this._syncLock) {
if(this._isDisposed) {
return;
}
if(disposing) {
this._tokenSource.Cancel();
this._tokenSource.Dispose();
}
this._isDisposed = true;
}
}
}
}
+200 -230
View File
@@ -1,236 +1,206 @@
using System;
using System.Threading;
namespace Swan.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 Int32 _period;
/// <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.
/// Initializes a new instance of the <see cref="ExclusiveTimer"/> class.
/// </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)
{
void Callback(IWaitEvent waitEvent)
{
try
{
waitEvent.Complete();
waitEvent.Begin();
}
catch
{
// ignore
}
}
using (var delayLock = WaitEventFactory.Create(true))
{
using (var _ = 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);
}
}
}
/// <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, Int32 dueTime, Int32 period) {
this._period = period;
this._userCallback = timerCallback;
this._backingTimer = new Timer(this.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, Int32 dueTime, Int32 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 Boolean IsDisposing => this._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 Boolean IsDisposed => this._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 IWaitEvent delayLock = WaitEventFactory.Create(true);
using ExclusiveTimer _ = 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(Int32 dueTime, Int32 period) {
this._period = period;
_ = this._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) => this.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(Int32 period) => this.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) => this.Change(TimeSpan.Zero, period);
/// <summary>
/// Pauses this instance.
/// </summary>
public void Pause() => this.Change(Timeout.Infinite, Timeout.Infinite);
/// <inheritdoc />
public void Dispose() {
lock(this._syncLock) {
if(this._isDisposed == true || this._isDisposing == true) {
return;
}
this._isDisposing.Value = true;
}
try {
this._cycleDoneEvent.Wait();
this._cycleDoneEvent.Dispose();
this.Pause();
this._backingTimer.Dispose();
} finally {
this._isDisposed.Value = true;
this._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(this._syncLock) {
if(this.IsDisposed || this.IsDisposing) {
return;
}
}
if(this._cycleDoneEvent.IsSet == false) {
return;
}
this._cycleDoneEvent.Reset();
try {
this._userCallback(state);
} finally {
this._cycleDoneEvent?.Set();
_ = this._backingTimer?.Change(this._period, Timeout.Infinite);
}
}
}
}
+17 -19
View File
@@ -1,24 +1,22 @@
using System;
namespace Swan.Threading
{
namespace Swan.Threading {
/// <summary>
/// Defines a generic interface for synchronized locking mechanisms.
/// </summary>
public interface ISyncLocker : IDisposable {
/// <summary>
/// Defines a generic interface for synchronized locking mechanisms.
/// Acquires a writer lock.
/// The lock is released when the returned locking object is disposed.
/// </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();
}
/// <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();
}
}
+58 -52
View File
@@ -1,57 +1,63 @@
using System;
namespace Swan.Threading
{
namespace Swan.Threading {
/// <summary>
/// Provides a generalized API for ManualResetEvent and ManualResetEventSlim.
/// </summary>
/// <seealso cref="IDisposable" />
public interface IWaitEvent : IDisposable {
/// <summary>
/// Provides a generalized API for ManualResetEvent and ManualResetEventSlim.
/// Gets a value indicating whether the event is in the completed state.
/// </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);
}
Boolean IsCompleted {
get;
}
/// <summary>
/// Gets a value indicating whether the Begin method has been called.
/// It returns false after the Complete method is called.
/// </summary>
Boolean IsInProgress {
get;
}
/// <summary>
/// Returns true if the underlying handle is not closed and it is still valid.
/// </summary>
Boolean IsValid {
get;
}
/// <summary>
/// Gets a value indicating whether this instance is disposed.
/// </summary>
Boolean 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>
Boolean Wait(TimeSpan timeout);
}
}
+71 -63
View File
@@ -1,69 +1,77 @@
using System;
using System.Threading.Tasks;
namespace Swan.Threading
{
namespace Swan.Threading {
/// <summary>
/// Defines a standard API to control background application workers.
/// </summary>
public interface IWorker {
/// <summary>
/// Defines a standard API to control background application workers.
/// Gets the current state of the worker.
/// </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();
}
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>
Boolean 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>
Boolean 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();
}
}
+15 -16
View File
@@ -1,21 +1,20 @@
using System.Threading;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Swan.Threading
{
namespace Swan.Threading {
/// <summary>
/// An interface for a worker cycle delay provider.
/// </summary>
public interface IWorkerDelayProvider {
/// <summary>
/// An interface for a worker cycle delay provider.
/// 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>
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);
}
/// <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(Int32 wantedDelay, Task delayTask, CancellationToken token);
}
}
+77 -93
View File
@@ -3,99 +3,83 @@ using System.Threading;
using System.Threading.Tasks;
using Swan.Logging;
namespace Swan.Threading
{
namespace Swan.Threading {
/// <summary>
/// Schedule an action to be periodically executed on the thread pool.
/// </summary>
public sealed class PeriodicTask : IDisposable {
/// <summary>
/// Schedule an action to be periodically executed on the thread pool.
/// <para>The minimum interval between action invocations.</para>
/// <para>The value of this field is equal to 100 milliseconds.</para>
/// </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));
}
}
}
}
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) {
this._action = action ?? throw new ArgumentNullException(nameof(action));
this._cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
this._interval = ValidateInterval(interval);
_ = Task.Run(this.ActionLoop);
}
/// <summary>
/// Finalizes an instance of the <see cref="PeriodicTask"/> class.
/// </summary>
~PeriodicTask() {
this.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 => this._interval;
set => this._interval = ValidateInterval(value);
}
/// <inheritdoc />
public void Dispose() {
this.Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(Boolean disposing) {
if(disposing) {
this._cancellationTokenSource.Cancel();
this._cancellationTokenSource.Dispose();
}
}
private static TimeSpan ValidateInterval(TimeSpan value)
=> value < MinInterval ? MinInterval : value;
private async Task ActionLoop() {
for(; ; )
{
try {
await Task.Delay(this.Interval, this._cancellationTokenSource.Token).ConfigureAwait(false);
await this._action(this._cancellationTokenSource.Token).ConfigureAwait(false);
} catch(OperationCanceledException) when(this._cancellationTokenSource.IsCancellationRequested) {
break;
} catch(TaskCanceledException) when(this._cancellationTokenSource.IsCancellationRequested) {
break;
} catch(Exception ex) {
ex.Log(nameof(PeriodicTask));
}
}
}
}
}
+156 -169
View File
@@ -4,175 +4,162 @@ using System.Threading;
using Swan.Configuration;
using Swan.Logging;
namespace Swan.Threading
{
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 {
private Thread _worker;
private CancellationTokenSource _cancelTokenSource;
private ManualResetEvent _workFinished;
/// <summary>
/// Represents an background worker abstraction with a life cycle and running at a independent thread.
/// Initializes a new instance of the <see cref="RunnerBase"/> class.
/// </summary>
public abstract class RunnerBase : ConfiguredObject
{
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);
}
catch (Exception ex)
{
$"{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))
{
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;
}
/// <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);
}
/// <param name="isEnabled">if set to <c>true</c> [is enabled].</param>
protected RunnerBase(Boolean isEnabled) {
this.Name = this.GetType().Name;
this.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 Boolean 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 Boolean IsEnabled {
get;
}
/// <summary>
/// Starts this instance.
/// </summary>
public virtual void Start() {
if(this.IsEnabled == false) {
return;
}
"Start Requested".Debug(this.Name);
this._cancelTokenSource = new CancellationTokenSource();
this._workFinished = new ManualResetEvent(false);
this._worker = new Thread(() => {
_ = this._workFinished.Reset();
this.IsRunning = true;
try {
this.Setup();
this.DoBackgroundWork(this._cancelTokenSource.Token);
} catch(ThreadAbortException) {
$"{nameof(ThreadAbortException)} caught.".Warn(this.Name);
} catch(Exception ex) {
$"{ex.GetType()}: {ex.Message}\r\n{ex.StackTrace}".Error(this.Name);
} finally {
this.Cleanup();
_ = this._workFinished?.Set();
this.IsRunning = false;
"Stopped Completely".Debug(this.Name);
}
}) {
IsBackground = true,
Name = $"{this.Name}Thread",
};
this._worker.Start();
}
/// <summary>
/// Stops this instance.
/// </summary>
public virtual void Stop() {
if(this.IsEnabled == false || this.IsRunning == false) {
return;
}
"Stop Requested".Debug(this.Name);
this._cancelTokenSource.Cancel();
Int32 waitRetries = 5;
while(waitRetries >= 1) {
if(this._workFinished.WaitOne(250)) {
waitRetries = -1;
break;
}
waitRetries--;
}
if(waitRetries < 0) {
"Workbench stopped gracefully".Debug(this.Name);
} else {
"Did not respond to stop request. Aborting thread and waiting . . .".Warn(this.Name);
this._worker.Abort();
if(this._workFinished.WaitOne(5000) == false) {
"Waited and no response. Worker might have been left in an inconsistent state.".Error(this.Name);
} else {
"Waited for worker and it finally responded (OK).".Debug(this.Name);
}
}
this._workFinished.Dispose();
this._workFinished = null;
}
/// <summary>
/// Setups this instance.
/// </summary>
protected void Setup() {
this.EnsureConfigurationNotLocked();
this.OnSetup();
this.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);
}
}
+173 -179
View File
@@ -1,185 +1,179 @@
using System;
using System.Threading;
namespace Swan.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>
/// Provides factory methods to create synchronized reader-writer locks
/// that support a generalized locking and releasing api and syntax.
/// Enumerates the locking operations.
/// </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
}
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(Boolean 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 Boolean _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) {
this._parent = parent;
this._operation = operation;
}
/// <inheritdoc />
public void Dispose() {
if(this._isDisposed) {
return;
}
this._isDisposed = true;
if(this._operation == LockHolderType.Read) {
this._parent.ReleaseReaderLock();
} else {
this._parent.ReleaseWriterLock();
}
}
}
/// <summary>
/// The Sync Locker backed by a ReaderWriterLock.
/// </summary>
/// <seealso cref="ISyncLocker" />
/// <seealso cref="ISyncReleasable" />
private sealed class SyncLocker : ISyncLocker, ISyncReleasable {
private Boolean _isDisposed;
private ReaderWriterLock _locker = new ReaderWriterLock();
/// <inheritdoc />
public IDisposable AcquireReaderLock() {
this._locker?.AcquireReaderLock(Timeout.Infinite);
return new SyncLockReleaser(this, LockHolderType.Read);
}
/// <inheritdoc />
public IDisposable AcquireWriterLock() {
this._locker?.AcquireWriterLock(Timeout.Infinite);
return new SyncLockReleaser(this, LockHolderType.Write);
}
/// <inheritdoc />
public void ReleaseWriterLock() => this._locker?.ReleaseWriterLock();
/// <inheritdoc />
public void ReleaseReaderLock() => this._locker?.ReleaseReaderLock();
/// <inheritdoc />
public void Dispose() {
if(this._isDisposed) {
return;
}
this._isDisposed = true;
_ = this._locker?.ReleaseLock();
this._locker = null;
}
}
/// <summary>
/// The Sync Locker backed by ReaderWriterLockSlim.
/// </summary>
/// <seealso cref="ISyncLocker" />
/// <seealso cref="ISyncReleasable" />
private sealed class SyncLockerSlim : ISyncLocker, ISyncReleasable {
private Boolean _isDisposed;
private ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
/// <inheritdoc />
public IDisposable AcquireReaderLock() {
this._locker?.EnterReadLock();
return new SyncLockReleaser(this, LockHolderType.Read);
}
/// <inheritdoc />
public IDisposable AcquireWriterLock() {
this._locker?.EnterWriteLock();
return new SyncLockReleaser(this, LockHolderType.Write);
}
/// <inheritdoc />
public void ReleaseWriterLock() => this._locker?.ExitWriteLock();
/// <inheritdoc />
public void ReleaseReaderLock() => this._locker?.ExitReadLock();
/// <inheritdoc />
public void Dispose() {
if(this._isDisposed) {
return;
}
this._isDisposed = true;
this._locker?.Dispose();
this._locker = null;
}
}
#endregion
}
}
+182 -213
View File
@@ -1,219 +1,188 @@
using System;
using System.Threading;
namespace Swan.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>
/// Provides a Manual Reset Event factory with a unified API.
/// Creates a Wait Event backed by a standard ManualResetEvent.
/// </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
{
get
{
if (IsValid == false)
return true;
return _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
{
get
{
if (IsDisposed || _event?.WaitHandle?.SafeWaitHandle == null) return false;
return !_event.WaitHandle.SafeWaitHandle.IsClosed && !_event.WaitHandle.SafeWaitHandle.IsInvalid;
}
}
/// <inheritdoc />
public bool IsCompleted => IsValid == false || _event.IsSet;
/// <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
}
/// <param name="isCompleted">if initially set to completed. Generally true.</param>
/// <returns>The Wait Event.</returns>
public static IWaitEvent Create(Boolean 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(Boolean 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(Boolean isCompleted, Boolean 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(Boolean isCompleted) => this._event = new ManualResetEvent(isCompleted);
/// <inheritdoc />
public Boolean IsDisposed {
get; private set;
}
/// <inheritdoc />
public Boolean IsValid => this.IsDisposed || this._event == null ? false : this._event?.SafeWaitHandle?.IsClosed ?? true ? false : !(this._event?.SafeWaitHandle?.IsInvalid ?? true);
/// <inheritdoc />
public Boolean IsCompleted => this.IsValid == false ? true : this._event?.WaitOne(0) ?? true;
/// <inheritdoc />
public Boolean IsInProgress => !this.IsCompleted;
/// <inheritdoc />
public void Begin() => this._event?.Reset();
/// <inheritdoc />
public void Complete() => this._event?.Set();
/// <inheritdoc />
void IDisposable.Dispose() {
if(this.IsDisposed) {
return;
}
this.IsDisposed = true;
_ = this._event?.Set();
this._event?.Dispose();
this._event = null;
}
/// <inheritdoc />
public void Wait() => this._event?.WaitOne();
/// <inheritdoc />
public Boolean Wait(TimeSpan timeout) => this._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(Boolean isCompleted) => this._event = new ManualResetEventSlim(isCompleted);
/// <inheritdoc />
public Boolean IsDisposed {
get; private set;
}
/// <inheritdoc />
public Boolean IsValid => this.IsDisposed || this._event?.WaitHandle?.SafeWaitHandle == null ? false : !this._event.WaitHandle.SafeWaitHandle.IsClosed && !this._event.WaitHandle.SafeWaitHandle.IsInvalid;
/// <inheritdoc />
public Boolean IsCompleted => this.IsValid == false || this._event.IsSet;
/// <inheritdoc />
public Boolean IsInProgress => !this.IsCompleted;
/// <inheritdoc />
public void Begin() => this._event?.Reset();
/// <inheritdoc />
public void Complete() => this._event?.Set();
/// <inheritdoc />
void IDisposable.Dispose() {
if(this.IsDisposed) {
return;
}
this.IsDisposed = true;
this._event?.Set();
this._event?.Dispose();
this._event = null;
}
/// <inheritdoc />
public void Wait() => this._event?.Wait();
/// <inheritdoc />
public Boolean Wait(TimeSpan timeout) => this._event?.Wait(timeout) ?? true;
}
#endregion
}
}
+28 -30
View File
@@ -1,33 +1,31 @@
namespace Swan.Threading
{
namespace Swan.Threading {
/// <summary>
/// Enumerates the different states in which a worker can be.
/// </summary>
public enum WorkerState {
/// <summary>
/// Enumerates the different states in which a worker can be.
/// The worker has been created and it is ready to start.
/// </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,
}
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,
}
}