DispatchAction() static private method

Send an action (encapsulated message).

If there is a transaction running on this thread, defer execution until the transaction ends (enqueue the action on the transaction).

If there is already an action running, enqueue it (nested).

Otherwise, queue it for execution.

static private DispatchAction ( System.Action action ) : void
action System.Action The action to execute.
return void
Beispiel #1
0
        /// <summary>
        /// Start a transaction and invoke a function.
        /// </summary>
        /// <param name="fn">The function to invoke.</param>
        /// <returns>The value computed by the function.</returns>
        object Run(IFn fn)
        {
            // TODO: Define an overload called on ThreadStartDelegate or something equivalent.

            bool          done   = false;
            object        ret    = null;
            List <Ref>    locked = new List <Ref>();
            List <Notify> notify = new List <Notify>();

            for (int i = 0; !done && i < RetryLimit; i++)
            {
                try
                {
                    GetReadPoint();
                    if (i == 0)
                    {
                        _startPoint = _readPoint;
                        _startTime  = Environment.TickCount;
                    }

                    _info = new Info(RUNNING, _startPoint);
                    ret   = fn.invoke();

                    // make sure no one has killed us before this point,
                    // and can't from now on
                    if (_info.Status.compareAndSet(RUNNING, COMMITTING))
                    {
                        foreach (KeyValuePair <Ref, List <CFn> > pair in _commutes)
                        {
                            Ref r = pair.Key;
                            if (_sets.Contains(r))
                            {
                                continue;
                            }

                            bool wasEnsured = _ensures.Contains(r);
                            // can't upgrade read lock, so release
                            ReleaseIfEnsured(r);
                            TryWriteLock(r);
                            locked.Add(r);

                            if (wasEnsured && r.CurrentValPoint() > _readPoint)
                            {
                                throw _retryex;
                            }

                            Info refinfo = r.TInfo;
                            if (refinfo != null && refinfo != _info && refinfo.IsRunning)
                            {
                                if (!Barge(refinfo))
                                {
                                    throw _retryex;
                                }
                            }
                            object val = r.TryGetVal();
                            _vals[r] = val;
                            foreach (CFn f in pair.Value)
                            {
                                _vals[r] = f.Fn.applyTo(RT.cons(_vals[r], f.Args));
                            }
                        }
                        foreach (Ref r in _sets)
                        {
                            TryWriteLock(r);
                            locked.Add(r);
                        }
                        // validate and enqueue notifications
                        foreach (KeyValuePair <Ref, object> pair in _vals)
                        {
                            Ref r = pair.Key;
                            r.Validate(pair.Value);
                        }

                        // at this point, all values calced, all refs to be written locked
                        // no more client code to be called
                        int  msecs       = System.Environment.TickCount;
                        long commitPoint = GetCommitPoint();
                        foreach (KeyValuePair <Ref, object> pair in _vals)
                        {
                            Ref    r      = pair.Key;
                            object oldval = r.TryGetVal();
                            object newval = pair.Value;

                            r.SetValue(newval, commitPoint, msecs);
                            if (r.getWatches().count() > 0)
                            {
                                notify.Add(new Notify(r, oldval, newval));
                            }
                        }

                        done = true;
                        _info.Status.set(COMMITTED);
                    }
                }
                catch (RetryEx)
                {
                    // eat this so we retry rather than fall out
                }
                catch (Exception ex)
                {
                    if (ContainsNestedRetryEx(ex))
                    {
                        // Wrapped exception, eat it.
                    }
                    else
                    {
                        throw;
                    }
                }
                finally
                {
                    for (int k = locked.Count - 1; k >= 0; --k)
                    {
                        locked[k].ExitWriteLock();
                    }
                    locked.Clear();
                    foreach (Ref r in _ensures)
                    {
                        r.ExitReadLock();
                    }
                    _ensures.Clear();
                    Stop(done ? COMMITTED : RETRY);
                    try
                    {
                        if (done) // re-dispatch out of transaction
                        {
                            foreach (Notify n in notify)
                            {
                                n._ref.NotifyWatches(n._oldval, n._newval);
                            }
                            foreach (Agent.Action action in _actions)
                            {
                                Agent.DispatchAction(action);
                            }
                        }
                    }
                    finally
                    {
                        notify.Clear();
                        _actions.Clear();
                    }
                }
            }
            if (!done)
            {
                throw new InvalidOperationException("Transaction failed after reaching retry limit");
            }
            return(ret);
        }