Beispiel #1
0
        /// <summary>
        /// Used by the slave to requests a new time interval from the source.
        /// This method blocks current thread until the time interval is granted.
        /// </summary>
        /// <remarks>
        /// This method will return immediately when the handle is disabled or detached.
        /// It is illegal to call this method twice in a row if the first call was successful (returned true). It must always be followed by calling <see cref="ReportBackAndContinue"> or <see cref="ReportBackAndBreak">.
        /// </remarks>
        /// <returns>
        /// True if the interval was granted or False when this call was interrupted as a result of detaching or disabling.
        /// If it returned true, <paramref name="interval"> contains the amount of virtual time to be used by the sink. It is the sum of time interval granted by the source (using <see cref="GrantInterval">) and a time left reported previously by <see cref="ReportBackAndContinue"> or <see cref="ReportBackAndBreak">.
        /// If it returned false, the time interval is not granted and it is illegal to report anything back using <see cref="ReportBackAndContinue"> or <see cref="ReportBackAndBreak">.
        /// </returns>
        public bool RequestTimeInterval(out TimeInterval interval)
        {
            this.Trace();
            lock (innerLock)
            {
                DebugHelper.Assert(!sinkSideInProgress, "Requested a new time interval, but the previous one is still processed.");

                var result = true;
                if (!Enabled || changingEnabled)
                {
                    // we test `changingEnabled` here to avoid starvation:
                    // in order to change state of `Enabled` property the handle must not be latched,
                    // so the operation blocks until `latchLevel` drops down to 0;
                    // calling this method (`RequestTimeInterval`) when the handle is in a blocking state results
                    // in latching it temporarily until `WaitUntilDone` is called;
                    // this temporary latching/unlatching together with normal latching/unlatching in a short loop
                    // can cause `latchLevel` to fluctuate from 1 to 2 never allowing the operation modifying `Enabled` to finish
                    result = false;
                }
                else if (isBlocking && SourceSideActive)
                {
                    // we check SourceSideActive here as otherwise unblocking will not succeed anyway
                    DebugHelper.Assert(!grantPending, "New grant not expected when blocked.");

                    this.Trace("Asking time source to unblock the time handle");
                    // latching here is to protect against disabling Enabled that would lead to making IsBlocking false while waiting for unblocking this handle
                    Latch();
                    Monitor.Exit(innerLock);
                    // it is necessary to leave `innerLock` since calling `UnblockHandle` could lead to a deadlock on `handles` collection
                    var isHandleUnblocked = TimeSource.UnblockHandle(this);
                    Monitor.Enter(innerLock);
                    if (!isHandleUnblocked)
                    {
                        Unlatch();
                        this.Trace("Unblocking handle is not allowed, quitting");
                        result = false;
                    }
                    else
                    {
                        this.Trace("Handle unblocked");
                        // since we are latched here, latchLevel 1 means that the handle is not currently latched by anybody else
                        // why? this is needed as we change the value of isBlocking - this should not happen when the handle is latched by another thread
                        this.Trace("About to wait until the latch reduces to 1");
                        innerLock.WaitWhile(() => latchLevel > 1, "Waiting for reducing the latch to 1");
                        isBlocking = false;

                        DebugHelper.Assert(!deferredUnlatch, "Unexpected value of deferredUnlatch");
                        deferredUnlatch   = true;
                        recentlyUnblocked = true;
                    }
                }
                else if (!grantPending)
                {
                    // wait until a new time interval is granted or this handle is disabled/deactivated
                    innerLock.WaitWhile(() => !grantPending && Enabled && SourceSideActive, "Waiting for a time grant");
                    result = grantPending;
                }

                if (!result)
                {
                    interval = TimeInterval.Empty;
                }
                else
                {
                    interval     = intervalGranted + timeResiduum;
                    timeResiduum = TimeInterval.Empty;

                    sinkSideInProgress = true;
                    grantPending       = false;
                }

                this.Trace($"{result}, {interval.Ticks}");
                return(result);
            }
        }