/// <summary>
        /// Test SemaphoreSlimDynamic Wait
        /// </summary>
        /// <param name="initial">The initial semaphore count</param>
        /// <param name="maximum">The maximum semaphore count</param>
        /// <param name="timeout">The timeout parameter for the wait method, it must be either int or TimeSpan</param>
        /// <param name="returnValue">The expected wait return value</param>
        /// <param name="exceptionType">The type of the thrown exception in case of invalid cases,
        /// null for valid cases</param>
        /// <returns>True if the test succeeded, false otherwise</returns>
        private static void RunSemaphoreSlimDynamicTest1_Wait_Internal
            (int initial, int maximum, object timeout, bool returnValue, Type exceptionType)
        {
            SemaphoreSlimDynamic semaphore = new SemaphoreSlimDynamic(0, initial, maximum);

            try
            {
                bool result = false;
                if (timeout is TimeSpan)
                {
                    result = semaphore.Wait((TimeSpan)timeout);
                }
                else
                {
                    result = semaphore.Wait((int)timeout);
                }
                Assert.Equal(returnValue, result);
                if (result)
                {
                    Assert.Equal(initial - 1, semaphore.CurrentCount);
                }
            }
            catch (Exception ex)
            {
                Assert.NotNull(exceptionType);
                Assert.IsType(exceptionType, ex);
            }
        }
        /// <summary>
        /// Call specific SemaphoreSlimDynamic method or property
        /// </summary>
        /// <param name="semaphore">The SemaphoreSlimDynamic instance</param>
        /// <param name="action">The action name</param>
        /// <param name="param">The action parameter, null if it takes no parameters</param>
        /// <param name="output">The test output helper to use if available.</param>
        /// <param name="outputPrefix">Prefix to add to the debug output.</param>
        /// <returns>The action return value, null if the action returns void</returns>
        private static object CallSemaphoreAction
            (SemaphoreSlimDynamic semaphore, Actions?action, object param, ITestOutputHelper output = null, string outputPrefix = null)
        {
            output?.WriteLine($"{outputPrefix ?? string.Empty}Action {action?.ToString() ?? "Unknown"}: {param?.ToString() ?? "None"}");

            if (action == Actions.Wait)
            {
                if (param is TimeSpan)
                {
                    return(semaphore.Wait((TimeSpan)param));
                }
                else if (param is int)
                {
                    return(semaphore.Wait((int)param));
                }
                semaphore.Wait();
                return(null);
            }
            else if (action == Actions.WaitAsync)
            {
                if (param is TimeSpan)
                {
                    return(semaphore.WaitAsync((TimeSpan)param).Result);
                }
                else if (param is int)
                {
                    return(semaphore.WaitAsync((int)param).Result);
                }
                semaphore.WaitAsync().Wait();
                return(null);
            }
            else if (action == Actions.Release)
            {
                if (param != null)
                {
                    return(semaphore.Release((int)param));
                }
                return(semaphore.Release());
            }
            else if (action == Actions.Dispose)
            {
                semaphore.Dispose();
                return(null);
            }
            else if (action == Actions.CurrentCount)
            {
                return(semaphore.CurrentCount);
            }
            else if (action == Actions.AvailableWaitHandle)
            {
                return(semaphore.AvailableWaitHandle);
            }
            else if (action == Actions.SetAvailableSlot)
            {
                return(semaphore.SetAvailableSlot((int)param));
            }

            return(null);
        }
        public static void RunSemaphoreSlimDynamicTest8_ConcWaitAndRelease(int initial, int maximum,
                                                                           int waitThreads, int releaseThreads, int succeededWait, int failedWait, int finalCount, int timeout)
        {
            SemaphoreSlimDynamic semaphore = new SemaphoreSlimDynamic(0, initial, maximum);

            Task[]           threads   = new Task[waitThreads + releaseThreads];
            int              succeeded = 0;
            int              failed    = 0;
            ManualResetEvent mre       = new ManualResetEvent(false);

            // launch threads
            for (int i = 0; i < threads.Length; i++)
            {
                if (i < waitThreads)
                {
                    // We are creating the Task using TaskCreationOptions.LongRunning to
                    // force usage of another thread (which will be the case on the default scheduler
                    // with its current implementation).  Without this, the release tasks will likely get
                    // queued behind the wait tasks in the pool, making it very likely that the wait tasks
                    // will starve the very tasks that when run would unblock them.
                    threads[i] = new Task(delegate()
                    {
                        mre.WaitOne();
                        if (semaphore.Wait(timeout))
                        {
                            Interlocked.Increment(ref succeeded);
                        }
                        else
                        {
                            Interlocked.Increment(ref failed);
                        }
                    }, TaskCreationOptions.LongRunning);
                }
                else
                {
                    threads[i] = new Task(delegate()
                    {
                        mre.WaitOne();
                        semaphore.Release();
                    });
                }
                threads[i].Start(TaskScheduler.Default);
            }

            mre.Set();
            //wait work to be done;
            Task.WaitAll(threads);
            //check the number of succeeded and failed wait
            Assert.Equal(succeededWait, succeeded);
            Assert.Equal(failedWait, failed);
            Assert.Equal(finalCount, semaphore.CurrentCount);
        }
        /// <summary>
        /// Test SemaphoreSlimDynamic WaitAsync
        /// The test verifies that SemaphoreSlimDynamic.Release() does not execute any user code synchronously.
        /// </summary>
        private static void RunSemaphoreSlimDynamicTest1_Wait_InternalAsync2()
        {
            SemaphoreSlimDynamic semaphore = new SemaphoreSlimDynamic(1, 1, 1);
            ThreadLocal <int>    counter   = new ThreadLocal <int>(() => 0);
            bool nonZeroObserved           = false;

            const int        asyncActions    = 20;
            int              remAsyncActions = asyncActions;
            ManualResetEvent mre             = new ManualResetEvent(false);

            Action <int> doWorkAsync = async delegate(int i)
            {
                await semaphore.WaitAsync();

                if (counter.Value > 0)
                {
                    nonZeroObserved = true;
                }

                counter.Value = counter.Value + 1;
                semaphore.Release();
                counter.Value = counter.Value - 1;

                if (Interlocked.Decrement(ref remAsyncActions) == 0)
                {
                    mre.Set();
                }
            };

            semaphore.Wait();
            for (int i = 0; i < asyncActions; i++)
            {
                doWorkAsync(i);
            }
            semaphore.Release();

            mre.WaitOne();

            Assert.False(nonZeroObserved, "RunSemaphoreSlimDynamicTest1_Wait_InternalAsync2:  FAILED.  SemaphoreSlimDynamic.Release() seems to have synchronously invoked a continuation.");
        }
        public static void RunSemaphoreSlimDynamicTest9_ConcurrentWaitAndWaitAsync(int syncWaiters, int asyncWaiters)
        {
            int totalWaiters = syncWaiters + asyncWaiters;

            var semaphore = new SemaphoreSlimDynamic(0, 0, int.MaxValue);

            Task[] tasks = new Task[totalWaiters];

            const int ITERS    = 10;
            int       randSeed = unchecked ((int)DateTime.Now.Ticks);

            for (int i = 0; i < syncWaiters; i++)
            {
                tasks[i] = Task.Run(delegate
                {
                    //Random rand = new Random(Interlocked.Increment(ref randSeed));
                    for (int iter = 0; iter < ITERS; iter++)
                    {
                        semaphore.Wait();
                        semaphore.Release();
                    }
                });
            }
            for (int i = syncWaiters; i < totalWaiters; i++)
            {
                tasks[i] = Task.Run(async delegate
                {
                    //Random rand = new Random(Interlocked.Increment(ref randSeed));
                    for (int iter = 0; iter < ITERS; iter++)
                    {
                        await semaphore.WaitAsync();
                        semaphore.Release();
                    }
                });
            }

            semaphore.Release(totalWaiters / 2);
            Task.WaitAll(tasks);
        }