From 21ffa816f18be4b765ad740ed5d93346ec3b1fda Mon Sep 17 00:00:00 2001 From: vnugent Date: Sat, 20 Jul 2024 19:44:31 -0400 Subject: static arugment list parsing functions --- lib/Utils/src/ArgumentList.cs | 73 +++++++++++++++++++++--- 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 /// /// Determines of the given argument is present in the argument list /// - /// + /// The name of the argument to check existence of /// A value that indicates if the argument is present in the list - public bool HasArgument(string arg) => _args.Contains(arg); + public bool HasArgument(string arg) => HasArgument(_args, arg); /// /// Determines if the argument is present in the argument list and @@ -89,11 +89,7 @@ namespace VNLib.Utils /// /// The argument to get following value of /// The argument value if found - 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); /// public IEnumerator GetEnumerator() => _args.GetEnumerator(); @@ -116,5 +112,68 @@ namespace VNLib.Utils string[] strings = Environment.GetCommandLineArgs(); return new ArgumentList(strings.Skip(1)); } + + /// + /// Determines of the given argument is present in the argument list + /// + /// The collection to search for the arugment within + /// The name of the argument to check existence of + /// A value that indicates if the argument is present in the list + public static bool HasArgument(T argsList, string argName) where T: IEnumerable + => argsList.Contains(argName, StringComparer.OrdinalIgnoreCase); + + /// + /// Determines if the argument is present in the argument list and + /// has a non-null value following it. + /// + /// The collection to search for the arugment within + /// The name of the argument to check existence of + /// A value that indicates if a non-null argument is present in the list + public static bool HasArgumentValue(T argsList, string argName) where T : IEnumerable + => GetArgument(argsList, argName) != null; + + /// + /// Gets the value following the specified argument, or + /// null no value follows the specified argument + /// + /// The collection to search for the arugment within + /// The name of the argument to check existence of + /// The argument value if found + public static string? GetArgument(T argsList, string argName) where T : IEnumerable + { + ArgumentNullException.ThrowIfNull(argsList); + + /* + * Try to optimize some fetching for types that have + * better performance for searching/indexing + */ + if (argsList is IList 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 @@ -37,20 +37,6 @@ namespace VNLib.Utils.Extensions /// public static class ThreadingExtensions { - /// - /// Allows an to execute within a scope limited context - /// - /// The resource type - /// - /// The function body that will execute with controlled access to the resource - public static void EnterSafeContext(this OpenResourceHandle rh, Action safeCallback) - { - using (rh) - { - safeCallback(rh.Resource); - } - } - /// /// Waits for exlcusive access to the resource identified by the given moniker /// and returns a handle that will release the lock when disposed. @@ -106,6 +92,7 @@ namespace VNLib.Utils.Extensions await semaphore.WaitAsync(cancellationToken); return new SemSlimReleaser(semaphore); } + /// /// Asynchronously waits to enter the 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); } + /// /// Blocks the current thread until it can enter the /// @@ -164,6 +152,7 @@ namespace VNLib.Utils.Extensions mutex.WaitOne(); return new MutexReleaser(mutex); } + /// /// Blocks the current thread until it can enter the /// @@ -201,6 +190,7 @@ namespace VNLib.Utils.Extensions public static Task 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); + } + + /// + /// Asynchronously waits for a the to receive a signal. This method spins until + /// a thread yield will occur, then asynchronously yields. + /// + /// + /// The timeout interval in milliseconds + /// A used to cancel the asynct wait event + /// + /// A task that compeletes when the wait handle receives a signal or times-out, + /// the result of the awaited task will be true if the signal is received, or + /// false if the timeout interval expires + /// + /// + /// + /// + public static Task WaitAsync(this WaitHandle handle, int timeoutMs, CancellationToken cancellation = default) + { + Task withoutToken = WaitAsync(handle, timeoutMs); + + return withoutToken.IsCompleted + ? withoutToken + : withoutToken.WaitAsync(cancellation); + } + + /// + /// Asynchronously waits for a the 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. + /// + /// + /// Time (in ms) + /// + public static Task NoSpinWaitAsync(this WaitHandle handle, int timeoutMs) + { //Completion source used to signal the awaiter when the wait handle is signaled TaskCompletionSource 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; } -- cgit