aboutsummaryrefslogtreecommitdiff
path: root/lib/Utils/src/Extensions
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Utils/src/Extensions')
-rw-r--r--lib/Utils/src/Extensions/ThreadingExtensions.cs75
1 files changed, 58 insertions, 17 deletions
diff --git a/lib/Utils/src/Extensions/ThreadingExtensions.cs b/lib/Utils/src/Extensions/ThreadingExtensions.cs
index a80a0ae..c29ab63 100644
--- a/lib/Utils/src/Extensions/ThreadingExtensions.cs
+++ b/lib/Utils/src/Extensions/ThreadingExtensions.cs
@@ -38,20 +38,6 @@ namespace VNLib.Utils.Extensions
public static class ThreadingExtensions
{
/// <summary>
- /// Allows an <see cref="OpenResourceHandle{TResource}"/> to execute within a scope limited context
- /// </summary>
- /// <typeparam name="TResource">The resource type</typeparam>
- /// <param name="rh"></param>
- /// <param name="safeCallback">The function body that will execute with controlled access to the resource</param>
- public static void EnterSafeContext<TResource>(this OpenResourceHandle<TResource> rh, Action<TResource> safeCallback)
- {
- using (rh)
- {
- safeCallback(rh.Resource);
- }
- }
-
- /// <summary>
/// Waits for exlcusive access to the resource identified by the given moniker
/// and returns a handle that will release the lock when disposed.
/// </summary>
@@ -106,6 +92,7 @@ namespace VNLib.Utils.Extensions
await semaphore.WaitAsync(cancellationToken);
return new SemSlimReleaser(semaphore);
}
+
/// <summary>
/// Asynchronously waits to enter the <see cref="SemaphoreSlim"/> using a 32-bit signed integer to measure the time intervale
/// and getting a releaser handle
@@ -135,6 +122,7 @@ namespace VNLib.Utils.Extensions
semaphore.Wait();
return new SemSlimReleaser(semaphore);
}
+
/// <summary>
/// Blocks the current thread until it can enter the <see cref="SemaphoreSlim"/>
/// </summary>
@@ -164,6 +152,7 @@ namespace VNLib.Utils.Extensions
mutex.WaitOne();
return new MutexReleaser(mutex);
}
+
/// <summary>
/// Blocks the current thread until it can enter the <see cref="SemaphoreSlim"/>
/// </summary>
@@ -201,6 +190,7 @@ namespace VNLib.Utils.Extensions
public static Task<bool> WaitAsync(this WaitHandle handle, int timeoutMs = Timeout.Infinite)
{
ArgumentNullException.ThrowIfNull(handle);
+
//test non-blocking handle state
if (handle.WaitOne(0))
{
@@ -223,13 +213,64 @@ namespace VNLib.Utils.Extensions
return TrueCompleted;
}
}
+
+ return NoSpinWaitAsync(handle, timeoutMs);
+ }
+
+ /// <summary>
+ /// Asynchronously waits for a the <see cref="WaitHandle"/> to receive a signal. This method spins until
+ /// a thread yield will occur, then asynchronously yields.
+ /// </summary>
+ /// <param name="handle"></param>
+ /// <param name="timeoutMs">The timeout interval in milliseconds</param>
+ /// <param name="cancellation">A <see cref="CancellationToken"/> used to cancel the asynct wait event</param>
+ /// <returns>
+ /// A task that compeletes when the wait handle receives a signal or times-out,
+ /// the result of the awaited task will be <c>true</c> if the signal is received, or
+ /// <c>false</c> if the timeout interval expires
+ /// </returns>
+ /// <exception cref="ArgumentNullException"></exception>
+ /// <exception cref="ObjectDisposedException"></exception>
+ /// <exception cref="ArgumentOutOfRangeException"></exception>
+ public static Task<bool> WaitAsync(this WaitHandle handle, int timeoutMs, CancellationToken cancellation = default)
+ {
+ Task<bool> withoutToken = WaitAsync(handle, timeoutMs);
+
+ return withoutToken.IsCompleted
+ ? withoutToken
+ : withoutToken.WaitAsync(cancellation);
+ }
+
+ /// <summary>
+ /// Asynchronously waits for a the <see cref="WaitHandle"/> to receive a signal, without checking
+ /// current state or spinning. This function always returns a new task that will complete when the
+ /// handle is signaled or the timeout interval expires.
+ /// </summary>
+ /// <param name="handle"></param>
+ /// <param name="timeoutMs">Time (in ms)</param>
+ /// <returns></returns>
+ public static Task<bool> NoSpinWaitAsync(this WaitHandle handle, int timeoutMs)
+ {
//Completion source used to signal the awaiter when the wait handle is signaled
TaskCompletionSource<bool> completion = new(TaskCreationOptions.None);
+
//Register wait on threadpool to complete the task source
- RegisteredWaitHandle registration = ThreadPool.RegisterWaitForSingleObject(handle, TaskCompletionCallback, completion, timeoutMs, true);
+ RegisteredWaitHandle registration = ThreadPool.RegisterWaitForSingleObject(
+ handle,
+ TaskCompletionCallback,
+ completion,
+ timeoutMs, executeOnlyOnce: true
+ );
+
//Register continuation to cleanup
- _ = completion.Task.ContinueWith(CleanupContinuation, registration, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default)
- .ConfigureAwait(false);
+ _ = completion.Task.ContinueWith(
+ CleanupContinuation,
+ registration,
+ CancellationToken.None,
+ TaskContinuationOptions.ExecuteSynchronously,
+ TaskScheduler.Default
+ ).ConfigureAwait(false);
+
return completion.Task;
}