Пример #1
0
        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
                        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);
                            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);
        }
Пример #2
0
 public Notify(Ref r, object oldval, object newval)
 {
     _ref    = r;
     _oldval = oldval;
     _newval = newval;
 }