Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<IsWindows>$([MSBuild]::IsOSPlatform('Windows'))</IsWindows>
<IsMainAuthor Condition="'$(EMAIL)' == 'michael@geekle.io'">true</IsMainAuthor>
<SkipSignAssembly>false</SkipSignAssembly>
<LangVersion>latest</LangVersion>
<LangVersion>preview</LangVersion>
</PropertyGroup>

<PropertyGroup Condition="'$(CI)' == 'true'">
Expand Down
4 changes: 4 additions & 0 deletions src/Cuemon.Core/Cuemon.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,8 @@
<PackageTags>action-factory bit-unit byte-unit binary-prefix decimal-prefix prefix-multiple multiple-table calculator configure configure-revert configure-exchange configurable condition options-pattern data-reader decorator delimited-string disposable finalize-disposable safe-invoke safe-invoke-async func-factory patterns reference-project clean-architecture clean-code task-action-factory task-func-factory template template-factory time-range time-unit validator guard text-encoding parser-factory security aes-cryptor cyclic-redundancy-check fowler-noll-vo-hash hash-factory hash-result hmac-message-digest hmac-secure-hash-algorithm keyed-crypto-hash keyed-crypto-algorithm message-digest non-crypto-algorithm secure-hash-algorithm unkeyed-crypto-hash</PackageTags>
</PropertyGroup>

<ItemGroup Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net9.0'))">
<Compile Remove="Threading\Lock.cs" />
</ItemGroup>

</Project>
3 changes: 2 additions & 1 deletion src/Cuemon.Core/Runtime/Dependency.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace Cuemon.Runtime
Expand All @@ -12,7 +13,7 @@ public abstract class Dependency : IDependency
{
private IEnumerable<IWatcher> _watchers;
private readonly Func<EventHandler<WatcherEventArgs>, IEnumerable<IWatcher>> _watchersHandler;
private readonly object _locker = new();
private readonly Lock _locker = new();

/// <summary>
/// Initializes a new instance of the <see cref="Dependency" /> class.
Expand Down
3 changes: 2 additions & 1 deletion src/Cuemon.Core/Runtime/FileWatcher.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Cuemon.Security;

Expand All @@ -11,7 +12,7 @@ namespace Cuemon.Runtime
/// <seealso cref="Watcher" />
public class FileWatcher : Watcher
{
private readonly object _locker = new();
private readonly Lock _locker = new();

/// <summary>
/// Initializes a new instance of the <see cref="FileWatcher"/> class.
Expand Down
2 changes: 1 addition & 1 deletion src/Cuemon.Core/Runtime/Watcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace Cuemon.Runtime
/// </summary>
public abstract class Watcher : Disposable, IWatcher
{
private readonly object _locker = new();
private readonly Lock _locker = new();
private Timer _watcherTimer;
private Timer _watcherPostponingTimer;

Expand Down
140 changes: 140 additions & 0 deletions src/Cuemon.Core/Threading/Lock.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// Credit: Mark Cilia Vincenti, 2024
// Taken from: https://github.com/MarkCiliaVincenti/Backport.System.Threading.Lock
// NuGet package: https://www.nuget.org/packages/Backport.System.Threading.Lock

using System.Runtime.CompilerServices;
using System.Security;
#if NET9_0_OR_GREATER
[assembly: TypeForwardedTo(typeof(System.Threading.Lock))]
#else
namespace System.Threading
{
/// <summary>
/// A backport of .NET 9.0+'s System.Threading.Lock. Provides a way to get mutual exclusion in regions of code between different threads.
/// A lock may be held by one thread at a time.
/// </summary>
/// <remarks>
/// Threads that cannot immediately enter the lock may wait for the lock to be exited or until a specified timeout. A thread
/// that holds a lock may enter the lock repeatedly without exiting it, such as recursively, in which case the thread should
/// eventually exit the lock the same number of times to fully exit the lock and allow other threads to enter the lock.
/// </remarks>
public sealed class Lock
{
#pragma warning disable CS9216 // A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement.
/// <summary>
/// <inheritdoc cref="Monitor.Enter(object)"/>
/// </summary>
/// <exception cref="ArgumentNullException"/>
#if !PRE_NETSTANDARD
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
public void Enter() => Monitor.Enter(this);

/// <summary>
/// <inheritdoc cref="Monitor.TryEnter(object)"/>
/// </summary>
/// <returns>
/// <inheritdoc cref="Monitor.TryEnter(object)"/>
/// </returns>
/// <exception cref="ArgumentNullException"/>
#if !PRE_NETSTANDARD
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
public bool TryEnter() => Monitor.TryEnter(this);

/// <summary>
/// <inheritdoc cref="Monitor.TryEnter(object, TimeSpan)"/>
/// </summary>
/// <returns>
/// <inheritdoc cref="Monitor.TryEnter(object, TimeSpan)"/>
/// </returns>
/// <exception cref="ArgumentNullException"/>
/// <exception cref="ArgumentOutOfRangeException"/>
#if !PRE_NETSTANDARD
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
public bool TryEnter(TimeSpan timeout) => Monitor.TryEnter(this, timeout);

/// <summary>
/// <inheritdoc cref="Monitor.TryEnter(object, int)"/>
/// </summary>
/// <returns>
/// <inheritdoc cref="Monitor.TryEnter(object, int)"/>
/// </returns>
/// <exception cref="ArgumentNullException"/>
/// <exception cref="ArgumentOutOfRangeException"/>
#if !PRE_NETSTANDARD
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
public bool TryEnter(int millisecondsTimeout) => Monitor.TryEnter(this, millisecondsTimeout);

/// <summary>
/// <inheritdoc cref="Monitor.Exit(object)"/>
/// </summary>
/// <exception cref="ArgumentNullException"/>
/// <exception cref="SynchronizationLockException"/>
#if !PRE_NETSTANDARD
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
public void Exit() => Monitor.Exit(this);

/// <summary>
/// Determines whether the current thread holds this lock.
/// </summary>
/// <returns>
/// true if the current thread holds this lock; otherwise, false.
/// </returns>
/// <exception cref="ArgumentNullException"/>
#if !PRE_NETSTANDARD
public bool IsHeldByCurrentThread => Monitor.IsEntered(this);
#else
public bool IsHeldByCurrentThread => throw new NotSupportedException("IsHeldByCurrentThread is only supported on .NET Framework 4.5 or greater.");
#endif
#pragma warning restore CS9216 // A value of type 'System.Threading.Lock' converted to a different type will use likely unintended monitor-based locking in 'lock' statement.

/// <summary>
/// Enters the lock and returns a <see cref="Scope"/> that may be disposed to exit the lock. Once the method returns,
/// the calling thread would be the only thread that holds the lock. This method is intended to be used along with a
/// language construct that would automatically dispose the <see cref="Scope"/>, such as with the C# using statement.
/// </summary>
/// <returns>
/// A <see cref="Scope"/> that may be disposed to exit the lock.
/// </returns>
/// <remarks>
/// If the lock cannot be entered immediately, the calling thread waits for the lock to be exited. If the lock is
/// already held by the calling thread, the lock is entered again. The calling thread should exit the lock, such as by
/// disposing the returned <see cref="Scope"/>, as many times as it had entered the lock to fully exit the lock and
/// allow other threads to enter the lock.
/// </remarks>
#if !PRE_NETSTANDARD
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
public Scope EnterScope()
{
Enter();
return new Scope(this);
}

/// <summary>
/// A disposable structure that is returned by <see cref="EnterScope()"/>, which when disposed, exits the lock.
/// </summary>
public ref struct Scope(Lock @lock)
{
/// <summary>
/// Exits the lock.
/// </summary>
/// <remarks>
/// If the calling thread holds the lock multiple times, such as recursively, the lock is exited only once. The
/// calling thread should ensure that each enter is matched with an exit.
/// </remarks>
/// <exception cref="SynchronizationLockException">
/// The calling thread does not hold the lock.
/// </exception>
#if !PRE_NETSTANDARD
[MethodImpl(MethodImplOptions.AggressiveInlining)]
#endif
public readonly void Dispose() => @lock.Exit();
}
}
}
#endif
3 changes: 2 additions & 1 deletion src/Cuemon.Data/BulkCopyDataReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading;

namespace Cuemon.Data
{
Expand All @@ -14,7 +15,7 @@ namespace Cuemon.Data
/// </summary>
public sealed class BulkCopyDataReader : DbDataReader
{
private static readonly object PadLock = new();
private static readonly Lock PadLock = new();
private IOrderedDictionary _defaultFields;

/// <summary>
Expand Down
3 changes: 2 additions & 1 deletion src/Cuemon.Data/DatabaseWatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Cuemon.Runtime;
using Cuemon.Security;
Expand All @@ -14,7 +15,7 @@ namespace Cuemon.Data
/// <seealso cref="Watcher" />
public class DatabaseWatcher : Watcher
{
private readonly object _locker = new();
private readonly Lock _locker = new();

/// <summary>
/// Initializes a new instance of the <see cref="DatabaseWatcher"/> class.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
using Cuemon.Extensions.AspNetCore.Newtonsoft.Json.Converters;
using Cuemon.Extensions.Newtonsoft.Json.Formatters;
using System.Threading;

namespace Cuemon.Extensions.AspNetCore.Newtonsoft.Json
{
internal static class Bootstrapper
{
private static readonly object PadLock = new();
private static readonly Lock PadLock = new();
private static bool _initialized;

internal static void Initialize()
Expand Down
3 changes: 2 additions & 1 deletion src/Cuemon.Extensions.AspNetCore.Text.Json/Bootstrapper.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
using Cuemon.Extensions.AspNetCore.Text.Json.Converters;
using Cuemon.Extensions.Text.Json.Formatters;
using System.Threading;

namespace Cuemon.Extensions.AspNetCore.Text.Json
{
internal static class Bootstrapper
{
private static readonly object PadLock = new();
private static readonly Lock PadLock = new();
private static bool _initialized;

internal static void Initialize()
Expand Down
3 changes: 2 additions & 1 deletion src/Cuemon.Extensions.AspNetCore.Xml/Bootstrapper.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
using Cuemon.Extensions.AspNetCore.Xml.Converters;
using Cuemon.Xml.Serialization.Formatters;
using System.Threading;

namespace Cuemon.Extensions.AspNetCore.Xml
{
internal static class Bootstrapper
{
private static readonly object PadLock = new();
private static readonly Lock PadLock = new();
private static bool _initialized;

internal static void Initialize()
Expand Down
2 changes: 1 addition & 1 deletion src/Cuemon.Extensions.Net/Http/SlimHttpClientFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public class SlimHttpClientFactory : IHttpClientFactory
private readonly ConcurrentDictionary<string, Lazy<ActiveHandler>> _activeHandlers = new();
private readonly ConcurrentQueue<ExpiredHandler> _expiredHandlers = new();
private readonly Func<HttpClientHandler> _handlerFactory;
private readonly object _locker = new();
private readonly Lock _locker = new();
private readonly SlimHttpClientFactoryOptions _options;
internal static readonly TimeSpan ExpirationTimerDueTime = TimeSpan.FromSeconds(15);
private Timer _expirationTimer;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Net.Http.Headers;
using System.Reflection;
using System.Threading;
using Cuemon.Configuration;
using Cuemon.Diagnostics;
using Cuemon.Extensions.Newtonsoft.Json.Converters;
Expand All @@ -16,7 +17,7 @@ namespace Cuemon.Extensions.Newtonsoft.Json.Formatters
/// </summary>
public class NewtonsoftJsonFormatterOptions : IExceptionDescriptorOptions, IContentNegotiation, IValidatableParameterObject
{
private readonly object _locker = new();
private readonly Lock _locker = new();
private bool _refreshed;

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Threading;
using Cuemon.Collections.Generic;
using Cuemon.Reflection;
using Cuemon.Runtime;
Expand Down Expand Up @@ -706,7 +707,7 @@ public static Func<T1, T2, T3, T4, T5, TResult> Memoize<TKey, T1, T2, T3, T4, T5
};
}

private static readonly object PadLock = new();
private static readonly Lock PadLock = new();

private static TResult Memoize<TKey, TTuple, TResult>(ICacheEnumerable<TKey> cache, string key, CacheInvalidation invalidation, FuncFactory<TTuple, TResult> valueFactory) where TTuple : Template
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using Cuemon.Configuration;
using Cuemon.Diagnostics;
using Cuemon.Extensions.Text.Json.Converters;
Expand All @@ -16,7 +17,7 @@ namespace Cuemon.Extensions.Text.Json.Formatters
/// </summary>
public class JsonFormatterOptions : IContentNegotiation, IExceptionDescriptorOptions, IValidatableParameterObject
{
private readonly object _locker = new();
private readonly Lock _locker = new();
private bool _refreshed;

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using System.Threading;
using Cuemon.Configuration;
using Cuemon.Diagnostics;
using Cuemon.Extensions.YamlDotNet.Converters;
Expand All @@ -15,7 +16,7 @@ namespace Cuemon.Extensions.YamlDotNet.Formatters
/// </summary>
public class YamlFormatterOptions : EncodingOptions, IExceptionDescriptorOptions, IContentNegotiation, IValidatableParameterObject
{
private readonly object _locker = new();
private readonly Lock _locker = new();
private bool _refreshed;

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Net.Http.Headers;
using System.Threading;
using Cuemon.Configuration;
using Cuemon.Diagnostics;
using Cuemon.Net.Http;
Expand All @@ -13,7 +14,7 @@ namespace Cuemon.Xml.Serialization.Formatters
/// </summary>
public class XmlFormatterOptions : IExceptionDescriptorOptions, IContentNegotiation, IValidatableParameterObject
{
private readonly object _locker = new();
private readonly Lock _locker = new();
private bool _refreshed;

/// <summary>
Expand Down