예제 #1
0
        private void InvokeContinuation()
        {
            Debug.Assert(_continuation != null);
            var continuation = _continuation !;

            switch (_capturedContext)
            {
            case null:
                if (RunContinuationsAsynchronously)
                {
#if TARGETS_NET || TARGETS_NETCORE || GREATERTHAN_NETSTANDARD13
                    if (_executionContext != null)
                    {
                        ThreadPoolEx.QueueUserWorkItem(continuation, _continuationState, true);
                        return;
                    }
#endif
                    ThreadPoolEx.QueueUserWorkItem(continuation, _continuationState, true);
                    return;
                }

                continuation(_continuationState);
                return;

            case SynchronizationContext sc:
                sc.Post(s =>
                {
                    var state = (Tuple <Action <object?>, object?>)s !;
                    state.Item1(state.Item2);
                }, Tuple.Create(continuation, _continuationState));
                return;

            case TaskScheduler ts:
#if NET40
                Task.Factory.StartNew(continuation, _continuationState, CancellationToken.None, TaskCreationOptions.None, ts);
#else
                Task.Factory.StartNew(continuation, _continuationState, CancellationToken.None, TaskCreationOptions.DenyChildAttach, ts);
#endif
                return;

            default:
                break;
            }
        }
예제 #2
0
        /// <summary>Schedules the continuation action for this operation.</summary>
        /// <param name="continuation">The continuation to invoke when the operation has completed.</param>
        /// <param name="state">The state object to pass to <paramref name="continuation"/> when it's invoked.</param>
        /// <param name="token">Opaque value that was provided to the <see cref="ValueTask"/>'s constructor.</param>
        /// <param name="flags">The flags describing the behavior of the continuation.</param>
        public void OnCompleted(Action <object?> continuation, object?state, short token, ValueTaskSourceOnCompletedFlags flags)
        {
            if (continuation == null)
            {
                throw new ArgumentNullException(nameof(continuation));
            }
            ValidateToken(token);

            if ((flags & ValueTaskSourceOnCompletedFlags.FlowExecutionContext) != 0)
            {
#if TARGETS_NET || TARGETS_NETCORE || GREATERTHAN_NETSTANDARD13
                _executionContext = ExecutionContext.Capture();
#endif
            }

            if ((flags & ValueTaskSourceOnCompletedFlags.UseSchedulingContext) != 0)
            {
                var sc = SynchronizationContext.Current;
                if (sc != null && sc.GetType() != typeof(SynchronizationContext))
                {
                    _capturedContext = sc;
                }
                else
                {
                    var ts = TaskScheduler.Current;
                    if (ts != TaskScheduler.Default)
                    {
                        _capturedContext = ts;
                    }
                }
            }

            // We need to set the continuation state before we swap in the delegate, so that
            // if there's a race between this and SetResult/Exception and SetResult/Exception
            // sees the _continuation as non-null, it'll be able to invoke it with the state
            // stored here.  However, this also means that if this is used incorrectly (e.g.
            // awaited twice concurrently), _continuationState might get erroneously overwritten.
            // To minimize the chances of that, we check preemptively whether _continuation
            // is already set to something other than the completion sentinel.

            object?oldContinuation = _continuation;
            if (oldContinuation == null)
            {
                _continuationState = state;
                oldContinuation    = Interlocked.CompareExchange(ref _continuation, continuation, null);
            }

            if (oldContinuation == null)
            {
                return;
            }

            // Operation already completed, so we need to queue the supplied callback.
            if (!ReferenceEquals(oldContinuation, ManualResetValueTaskSourceCoreShared.Sentinel))
            {
                throw new InvalidOperationException();
            }

            switch (_capturedContext)
            {
            case null:
#if TARGETS_NET || TARGETS_NETCORE || GREATERTHAN_NETSTANDARD13
                if (_executionContext != null)
                {
                    ThreadPoolEx.QueueUserWorkItem(continuation, state, true);
                    return;
                }
#endif
                ThreadPoolEx.UnsafeQueueUserWorkItem(continuation, state, true);
                return;

            case SynchronizationContext sc:
                sc.Post(s =>
                {
                    var tuple = (Tuple <Action <object?>, object?>)s !;
                    tuple.Item1(tuple.Item2);
                }, Tuple.Create(continuation, state));
                return;

            case TaskScheduler ts:
#if NET40
                Task.Factory.StartNew(continuation, state, CancellationToken.None, TaskCreationOptions.None, ts);
#else
                Task.Factory.StartNew(continuation, state, CancellationToken.None, TaskCreationOptions.DenyChildAttach, ts);
#endif
                return;
            }
        }
예제 #3
0
        /// <summary>
        /// The main processing loop.
        /// </summary>
        public void Run()
        {
            bool lastAcquireFailed = false;

            while (!_halted)
            {
                try {
                    // check if we're supposed to pause...
                    lock (_pauseLock) {
                        while (_paused && !_halted)
                        {
                            try {
                                // wait until togglePause(false) is called...
                                Monitor.Wait(_pauseLock, 100);
                            } catch (ThreadInterruptedException) {
                            }
                        }
                        if (_halted)
                        {
                            break;
                        }
                    }

                    int      availTreadCount = _threadPool.AvailableThreads;
                    DateTime now;
                    int      spinInterval;
                    int      numPauses;
                    if (availTreadCount > 0)
                    {
                        Trigger trigger = null;

                        now = DateTime.UtcNow;

                        _signaled = false;
                        try {
                            trigger           = AcquireNextTrigger(now.AddMilliseconds(_idleWaitTime));
                            lastAcquireFailed = false;
                        } catch (Exception e) {
                            if (!lastAcquireFailed)
                            {
                                log.Error("SchedulerThreadLoop: RuntimeException " + e.Message, e);
                            }
                            lastAcquireFailed = true;
                        }

                        if (trigger != null)
                        {
                            now = DateTime.UtcNow;
                            DateTime triggerTime      = trigger.GetNextFireTimeUtc().Value;
                            long     timeUntilTrigger = (long)(triggerTime - now).TotalMilliseconds;
                            spinInterval = 10;

                            // this looping may seem a bit silly, but it's the
                            // current work-around
                            // for a dead-lock that can occur if the Thread.sleep()
                            // is replaced with
                            // a obj.wait() that gets notified when the signal is
                            // set...
                            // so to be able to detect the signal change without
                            // sleeping the entire
                            // timeUntilTrigger, we spin here... don't worry
                            // though, this spinning
                            // doesn't even register 0.2% cpu usage on a pentium 4.
                            numPauses = (int)(timeUntilTrigger / spinInterval);
                            while (numPauses >= 0 && !_signaled)
                            {
                                try {
                                    Thread.Sleep(spinInterval);
                                } catch (ThreadInterruptedException) {
                                }

                                now = DateTime.UtcNow;
                                timeUntilTrigger = (long)(triggerTime - now).TotalMilliseconds;
                                numPauses        = (int)(timeUntilTrigger / spinInterval);
                            }
                            if (_signaled)
                            {
                                try {
                                    ReleaseAcquiredTrigger(trigger);
                                } catch (Exception ex) {
                                    log.Error("ReleaseAcquiredTrigger: RuntimeException " + ex.Message, ex);
                                }
                                _signaled = false;
                                continue;
                            }

                            // set trigger to 'executing'
                            TriggerFiredBundle bundle = null;

                            lock (_pauseLock) {
                                if (!_halted)
                                {
                                    try {
                                        bundle = TriggerFired(trigger);
                                    } catch (Exception ex) {
                                        log.Error(string.Format(CultureInfo.InvariantCulture, "RuntimeException while firing trigger {0}", trigger.Name), ex);
                                    }
                                }

                                // it's possible to get 'null' if the trigger was paused,
                                // blocked, or other similar occurances that prevent it being
                                // fired at this time...  or if the scheduler was shutdown (halted)
                                if (bundle == null)
                                {
                                    try {
                                        ReleaseAcquiredTrigger(trigger);
                                    } catch (SchedulerException) {
                                    }
                                    continue;
                                }

                                _threadPool.QueueUserWorkItem(new WaitCallback(ProcessJob), bundle);
                            }

                            continue;
                        }
                    }
                    else
                    {
                        // if(availTreadCount > 0)
                        continue;                         // should never happen, if threadPool.blockForAvailableThreads() follows contract
                    }

                    // this looping may seem a bit silly, but it's the current
                    // work-around
                    // for a dead-lock that can occur if the Thread.sleep() is replaced
                    // with
                    // a obj.wait() that gets notified when the signal is set...
                    // so to be able to detect the signal change without sleeping the
                    // entier
                    // getRandomizedIdleWaitTime(), we spin here... don't worry though,
                    // the
                    // CPU usage of this spinning can't even be measured on a pentium
                    // 4.
                    now = DateTime.UtcNow;
                    DateTime waitTime          = now.AddMilliseconds(GetRandomizedIdleWaitTime());
                    long     timeUntilContinue = (long)(waitTime - now).TotalMilliseconds;
                    spinInterval = 10;
                    numPauses    = (int)(timeUntilContinue / spinInterval);

                    while (numPauses > 0 && !_signaled)
                    {
                        try {
                            Thread.Sleep(10);
                        } catch (ThreadInterruptedException) {
                        }
                        now = DateTime.UtcNow;
                        timeUntilContinue = (long)(waitTime - now).TotalMilliseconds;
                        numPauses         = (int)(timeUntilContinue / spinInterval);
                    }
                } catch (ThreadAbortException) {
                } catch (Exception ex) {
                    log.Error("Runtime error occured in main trigger firing loop.", ex);
                }
            }
        }