aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/Utils/src/ArgumentList.cs73
-rw-r--r--lib/Utils/src/Extensions/ThreadingExtensions.cs75
2 files changed, 124 insertions, 24 deletions
diff --git a/lib/Utils/src/ArgumentList.cs b/lib/Utils/src/ArgumentList.cs
index 7226b27..0f092c6 100644
--- a/lib/Utils/src/ArgumentList.cs
+++ b/lib/Utils/src/ArgumentList.cs
@@ -71,9 +71,9 @@ namespace VNLib.Utils
/// <summary>
/// Determines of the given argument is present in the argument list
/// </summary>
- /// <param name="arg"></param>
+ /// <param name="arg">The name of the argument to check existence of</param>
/// <returns>A value that indicates if the argument is present in the list</returns>
- public bool HasArgument(string arg) => _args.Contains(arg);
+ public bool HasArgument(string arg) => HasArgument(_args, arg);
/// <summary>
/// Determines if the argument is present in the argument list and
@@ -89,11 +89,7 @@ namespace VNLib.Utils
/// </summary>
/// <param name="arg">The argument to get following value of</param>
/// <returns>The argument value if found</returns>
- public string? GetArgument(string arg)
- {
- int index = _args.IndexOf(arg);
- return index == -1 || index + 1 >= _args.Count ? null : this[index + 1];
- }
+ public string? GetArgument(string arg) => GetArgument(_args, arg);
///<inheritdoc/>
public IEnumerator<string> GetEnumerator() => _args.GetEnumerator();
@@ -116,5 +112,68 @@ namespace VNLib.Utils
string[] strings = Environment.GetCommandLineArgs();
return new ArgumentList(strings.Skip(1));
}
+
+ /// <summary>
+ /// Determines of the given argument is present in the argument list
+ /// </summary>
+ /// <param name="argsList">The collection to search for the arugment within</param>
+ /// <param name="argName">The name of the argument to check existence of</param>
+ /// <returns>A value that indicates if the argument is present in the list</returns>
+ public static bool HasArgument<T>(T argsList, string argName) where T: IEnumerable<string>
+ => argsList.Contains(argName, StringComparer.OrdinalIgnoreCase);
+
+ /// <summary>
+ /// Determines if the argument is present in the argument list and
+ /// has a non-null value following it.
+ /// </summary>
+ /// <param name="argsList">The collection to search for the arugment within</param>
+ /// <param name="argName">The name of the argument to check existence of</param>
+ /// <returns>A value that indicates if a non-null argument is present in the list</returns>
+ public static bool HasArgumentValue<T>(T argsList, string argName) where T : IEnumerable<string>
+ => GetArgument(argsList, argName) != null;
+
+ /// <summary>
+ /// Gets the value following the specified argument, or
+ /// null no value follows the specified argument
+ /// </summary>
+ /// <param name="argsList">The collection to search for the arugment within</param>
+ /// <param name="argName">The name of the argument to check existence of</param>
+ /// <returns>The argument value if found</returns>
+ public static string? GetArgument<T>(T argsList, string argName) where T : IEnumerable<string>
+ {
+ ArgumentNullException.ThrowIfNull(argsList);
+
+ /*
+ * Try to optimize some fetching for types that have
+ * better performance for searching/indexing
+ */
+ if (argsList is IList<string> argList)
+ {
+ int index = argList.IndexOf(argName);
+ return index == -1 || index + 1 >= argList.Count
+ ? null
+ : argList[index + 1];
+ }
+ else if(argsList is string[] argsArr)
+ {
+ return findInArray(argsArr, argName);
+ }
+ else
+ {
+ //TODO use linq instead of converting to array on every call
+ return findInArray(
+ argsList.ToArray(),
+ argName
+ );
+ }
+
+ static string? findInArray(string[] argsArr, string argName)
+ {
+ int index = Array.IndexOf(argsArr, argName);
+ return index == -1 || index + 1 >= argsArr.Length
+ ? null
+ : argsArr[index + 1];
+ }
+ }
}
} \ No newline at end of file
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;
}