<commit_before>﻿#if NET40

using System;
using System.Threading;
using System.Threading.Tasks;
using Theraot.Threading;

namespace Theraot.Core
{
    public static class SemaphoreSlimExtensions
    {
        public static Task WaitAsync(this SemaphoreSlim semaphore)
        {
            return WaitAsync(semaphore, Timeout.Infinite, CancellationToken.None);
        }

        public static Task WaitAsync(this SemaphoreSlim semaphore, CancellationToken cancellationToken)
        {
            return WaitAsync(semaphore, Timeout.Infinite, cancellationToken);
        }

        public static Task<bool> WaitAsync(this SemaphoreSlim semaphore, int millisecondsTimeout)
        {
            return WaitAsync(semaphore, millisecondsTimeout, CancellationToken.None);
        }

        public static Task<bool> WaitAsync(this SemaphoreSlim semaphore, TimeSpan timeout)
        {
            if (semaphore == null)
            {
                throw new ArgumentNullException(nameof(semaphore));
            }
            GC.KeepAlive(semaphore.AvailableWaitHandle);
            return WaitAsyncPrivate(semaphore, (int)timeout.TotalMilliseconds, CancellationToken.None);
        }

        public static Task<bool> WaitAsync(this SemaphoreSlim semaphore, TimeSpan timeout, CancellationToken cancellationToken)
        {
            if (semaphore == null)
            {
                throw new ArgumentNullException(nameof(semaphore));
            }
            GC.KeepAlive(semaphore.AvailableWaitHandle);
            return WaitAsyncPrivate(semaphore, (int)timeout.TotalMilliseconds, cancellationToken);
        }

        public static Task<bool> WaitAsync(this SemaphoreSlim semaphore, int millisecondsTimeout, CancellationToken cancellationToken)
        {
            if (semaphore == null)
            {
                throw new ArgumentNullException(nameof(semaphore));
            }
            GC.KeepAlive(semaphore.AvailableWaitHandle);
            return WaitAsyncPrivate(semaphore, millisecondsTimeout, cancellationToken);
        }

        private static Task<bool> WaitAsyncPrivate(SemaphoreSlim semaphore, int millisecondsTimeout, CancellationToken cancellationToken)
        {
            if (millisecondsTimeout < -1)
            {
                throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout));
            }
            if (cancellationToken.IsCancellationRequested)
            {
                return TaskEx.FromCanceled<bool>(cancellationToken);
            }
            var source = new TaskCompletionSource<bool>();
            if (semaphore.Wait(0))
            {
                source.SetResult(true);
            }
            else
            {
                var waitHandle = semaphore.AvailableWaitHandle;
                var registration = new RegisteredWaitHandle[1];
                if (millisecondsTimeout == -1)
                {
                    registration[0] = ThreadPool.RegisterWaitForSingleObject(waitHandle, CallbackWithoutTimeout, null, -1, true);
                    void CallbackWithoutTimeout(object state, bool timeOut)
                    {
                        Unregister();
                        if (cancellationToken.IsCancellationRequested)
                        {
                            source.TrySetCanceled();
                            return;
                        }
                        if (semaphore.Wait(0))
                        {
                            source.TrySetResult(true);
                            return;
                        }
                        var newRegistration = ThreadPool.RegisterWaitForSingleObject(waitHandle, CallbackWithoutTimeout, null, -1, true);
                        Volatile.Write(ref registration[0], newRegistration);
                    }
                }
                else
                {
                    var start = ThreadingHelper.TicksNow();
                    registration[0] = ThreadPool.RegisterWaitForSingleObject(waitHandle, CallbackWithTimeout, null, millisecondsTimeout, true);
                    void CallbackWithTimeout(object state, bool timeOut)
                    {
                        Unregister();
                        if (cancellationToken.IsCancellationRequested)
                        {
                            source.TrySetCanceled();
                            return;
                        }
                        long timeout;
                        if (timeOut || (timeout = millisecondsTimeout - ThreadingHelper.Milliseconds(ThreadingHelper.TicksNow() - start)) <= 0)
                        {
                            source.TrySetResult(false);
                            return;
                        }
                        if (semaphore.Wait(0))
                        {
                            source.TrySetResult(true);
                            return;
                        }
                        var newRegistration = ThreadPool.RegisterWaitForSingleObject(waitHandle, CallbackWithTimeout, null, timeout, true);
                        Volatile.Write(ref registration[0], newRegistration);
                    }
                }
                cancellationToken.Register(Unregister);
                void Unregister()
                {
                    Volatile.Read(ref registration[0]).Unregister(null);
                }
            }
            return source.Task;
        }
    }
}

#endif<commit_msg>Reworked SemaphoreSlimExtensions based on TaskEx.FromWaitHandle<commit_after>﻿#if NET40

using System;
using System.Threading;
using System.Threading.Tasks;
using Theraot.Threading;

namespace Theraot.Core
{
    public static class SemaphoreSlimExtensions
    {
        public static Task WaitAsync(this SemaphoreSlim semaphore)
        {
            if (semaphore == null)
            {
                throw new ArgumentNullException(nameof(semaphore));
            }
            GC.KeepAlive(semaphore.AvailableWaitHandle);
            return WaitAsyncPrivate(semaphore);
        }

        public static Task WaitAsync(this SemaphoreSlim semaphore, CancellationToken cancellationToken)
        {
            if (semaphore == null)
            {
                throw new ArgumentNullException(nameof(semaphore));
            }
            GC.KeepAlive(semaphore.AvailableWaitHandle);
            return WaitAsyncPrivate(semaphore, cancellationToken);
        }

        public static Task<bool> WaitAsync(this SemaphoreSlim semaphore, int millisecondsTimeout)
        {
            if (semaphore == null)
            {
                throw new ArgumentNullException(nameof(semaphore));
            }
            GC.KeepAlive(semaphore.AvailableWaitHandle);
            if (millisecondsTimeout < -1)
            {
                throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout));
            }
            if (millisecondsTimeout == -1)
            {
                return WaitAsyncPrivate(semaphore);
            }
            return WaitAsyncPrivate(semaphore, millisecondsTimeout);
        }

        public static Task<bool> WaitAsync(this SemaphoreSlim semaphore, TimeSpan timeout)
        {
            if (semaphore == null)
            {
                throw new ArgumentNullException(nameof(semaphore));
            }
            GC.KeepAlive(semaphore.AvailableWaitHandle);
            var millisecondsTimeout = (int)timeout.TotalMilliseconds;
            if (millisecondsTimeout < -1)
            {
                throw new ArgumentOutOfRangeException(nameof(timeout));
            }
            if (millisecondsTimeout == -1)
            {
                return WaitAsyncPrivate(semaphore);
            }
            return WaitAsyncPrivate(semaphore, millisecondsTimeout);
        }

        public static Task<bool> WaitAsync(this SemaphoreSlim semaphore, TimeSpan timeout, CancellationToken cancellationToken)
        {
            if (semaphore == null)
            {
                throw new ArgumentNullException(nameof(semaphore));
            }
            GC.KeepAlive(semaphore.AvailableWaitHandle);
            var millisecondsTimeout = (int)timeout.TotalMilliseconds;
            if (millisecondsTimeout < -1)
            {
                throw new ArgumentOutOfRangeException(nameof(timeout));
            }
            if (millisecondsTimeout == -1)
            {
                return WaitAsyncPrivate(semaphore, cancellationToken);
            }
            return WaitAsyncPrivate(semaphore, (int)timeout.TotalMilliseconds, cancellationToken);
        }

        public static Task<bool> WaitAsync(this SemaphoreSlim semaphore, int millisecondsTimeout, CancellationToken cancellationToken)
        {
            if (semaphore == null)
            {
                throw new ArgumentNullException(nameof(semaphore));
            }
            GC.KeepAlive(semaphore.AvailableWaitHandle);
            if (millisecondsTimeout < -1)
            {
                throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout));
            }
            if (millisecondsTimeout == -1)
            {
                return WaitAsyncPrivate(semaphore, cancellationToken);
            }
            return WaitAsyncPrivate(semaphore, millisecondsTimeout, cancellationToken);
        }

        private static async Task<bool> WaitAsyncPrivate(SemaphoreSlim semaphore)
        {
            if (semaphore.Wait(0))
            {
                return true;
            }
            while (true)
            {
                await TaskEx.FromWaitHandleInternal(semaphore.AvailableWaitHandle);
                if (semaphore.Wait(0))
                {
                    return true;
                }
            }
        }

        private static async Task<bool> WaitAsyncPrivate(SemaphoreSlim semaphore, int millisecondsTimeout)
        {
            if (semaphore.Wait(0))
            {
                return true;
            }
            var start = ThreadingHelper.TicksNow();
            var timeout = millisecondsTimeout;
            while
            (
                await TaskEx.FromWaitHandleInternal
                (
                    semaphore.AvailableWaitHandle,
                    timeout
                )
            )
            {
                if (semaphore.Wait(0))
                {
                    return true;
                }
                timeout = (int)(millisecondsTimeout - ThreadingHelper.Milliseconds(ThreadingHelper.TicksNow() - start));
            }
            return false;
        }

        private static async Task<bool> WaitAsyncPrivate(SemaphoreSlim semaphore, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            if (semaphore.Wait(0))
            {
                return true;
            }
            while (true)
            {
                await TaskEx.FromWaitHandleInternal(semaphore.AvailableWaitHandle, cancellationToken);
                if (semaphore.Wait(0))
                {
                    return true;
                }
            }
        }

        private static async Task<bool> WaitAsyncPrivate(SemaphoreSlim semaphore, int millisecondsTimeout, CancellationToken cancellationToken)
        {
            if (semaphore.Wait(0))
            {
                return true;
            }
            var start = ThreadingHelper.TicksNow();
            var timeout = millisecondsTimeout;
            while
            (
                timeout > 0 &&
                await TaskEx.FromWaitHandleInternal
                (
                    semaphore.AvailableWaitHandle,
                    timeout,
                    cancellationToken
                )
            )
            {
                if (semaphore.Wait(0))
                {
                    return true;
                }
                timeout = (int)(millisecondsTimeout - ThreadingHelper.Milliseconds(ThreadingHelper.TicksNow() - start));
            }
            return false;
        }
    }
}

#endif
{'fraction_non_alphanumeric': 0.06944329212644156, 'fraction_numerical': 0.0029038413672944495, 'mean_word_length': 1.4726153846153847, 'pattern_counts': {'":': 0, '<': 24, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5007831', 'n_tokens_mistral': 2915, 'n_tokens_neox': 2832, 'n_words': 525}