diff options
Diffstat (limited to 'lib/Utils/src')
-rw-r--r-- | lib/Utils/src/ArgumentList.cs | 73 | ||||
-rw-r--r-- | lib/Utils/src/Extensions/ThreadingExtensions.cs | 75 |
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; } |