Esempio n. 1
0
        /// <summary>
        /// Prevents parallel execution of the FuncAsync
        /// </summary>
        /// <param name="func">Func to lock</param>
        /// <param name="mode">Mode to use for locking</param>
        /// <returns>A FuncAsync which cannot have nmultiple instance running at a same time</returns>
        public static FuncAsync <TResult> LockInvocation <TResult>(this FuncAsync <TResult> func, InvocationLockingMode mode = InvocationLockingMode.Share)
        {
            // Note: Do not use TaskCompletionSource, for strange reasons it cause app crashes on iOS (on SetException).
            // Prefer keep task by themselves instead of trying to replicate task state to a TaskCompletionSource.

            if (mode == InvocationLockingMode.Share)
            {
                var gate    = new object();
                var pending = default(Task <TResult>);

                return(async ct =>
                {
                    var created = false;
                    try
                    {
                        var task = pending;
                        if (task == null)
                        {
                            lock (gate)
                            {
                                task = pending;
                                if (task == null)
                                {
                                    created = true;
                                    task = pending = func(ct);
                                }
                            }
                        }

                        return await task;
                    }
                    finally
                    {
                        // Note: Keep trace of the creation and let to the initiator the responsibility to removed the task from pendings
                        // DO NOT auto remove at the end of the task itself: If the task run synchronously, we might TryRemove the task before it is actually added to the pendings.
                        if (created)
                        {
                            pending = null;
                        }
                    }
                });
            }
            else
            {
                var gate = new AsyncLock();

                return(async ct =>
                {
                    using (await gate.LockAsync(ct))
                    {
                        return await func(ct);
                    }
                });
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Prevents parallel execution of the FuncAsync for a SAME PARAMETER
        /// </summary>
        /// <param name="func">Func to lock</param>
        /// <param name="mode">Mode to use for locking FOR A SAME PARAMETER</param>
        /// <returns>A FuncAsync which cannot have nmultiple instance running at a same time</returns>
        public static FuncAsync <TParam, TResult> LockInvocation <TParam, TResult>(this FuncAsync <TParam, TResult> func, InvocationLockingMode mode = InvocationLockingMode.Share)
            where TParam : class
        {
            // Note: Do not use TaskCompletionSource, for strange reasons it cause app crashes on iOS (on SetException).
            // Prefer keep task by themselves instead of trying to replicate task state to a TaskCompletionSource.

            if (mode == InvocationLockingMode.Share)
            {
#if HAS_NO_CONCURRENT_DICT
                var pendings = new SynchronizedDictionary <TParam, Task <TResult> >();
#else
                var pendings = new System.Collections.Concurrent.ConcurrentDictionary <TParam, Task <TResult> >();
#endif

                return(async(ct, param) =>
                {
                    var created = false;
                    try
                    {
                        return await pendings.GetOrAdd(param, p =>
                        {
                            created = true;
                            return func(ct, p);
                        });
                    }
                    finally
                    {
                        // Note: Keep trace of the creation and let to the initiator the responsibility to removed the task from pendings
                        // DO NOT auto remove at the end of the task itself: If the task run synchronously, we might TryRemove the task before it is actually added to the pendings.
                        if (created)
                        {
                            Task <TResult> _;
                            pendings.TryRemove(param, out _);
                        }
                    }
                });
            }
            else
            {
                var gates = new ConditionalWeakTable <TParam, AsyncLock>();

                return(async(ct, param) =>
                {
                    var gate = gates.GetValue(param, _ => new AsyncLock());
                    using (await gate.LockAsync(ct))
                    {
                        return await func(ct, param);
                    }
                });
            }
        }