Ejemplo n.º 1
0
        /// <summary>
        /// Lock a ref.
        /// </summary>
        /// <param name="r">The ref to lock.</param>
        /// <returns>The most recent value of the ref.</returns>
        object Lock(Ref r)
        {
            // can't upgrade read lock, so release it.
            ReleaseIfEnsured(r);

            bool unlocked = true;

            try
            {
                TryWriteLock(r);
                unlocked = false;

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

                Info refinfo = r.TInfo;

                // write lock conflict
                if (refinfo != null && refinfo != _info && refinfo.IsRunning)
                {
                    if (!Barge(refinfo))
                    {
                        r.ExitWriteLock();
                        unlocked = true;
                        return(BlockAndBail(refinfo));
                    }
                }

                r.TInfo = _info;
                return(r.TryGetVal());
            }
            finally
            {
                if (!unlocked)
                {
                    r.ExitWriteLock();
                }
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Touch a ref.  (Lock it.)
        /// </summary>
        /// <param name="r">The ref to touch.</param>
        internal void DoEnsure(Ref r)
        {
            if (!_info.IsRunning)
            {
                throw _retryex;
            }
            if (_ensures.Contains(r))
            {
                return;
            }

            Lock(r);

            // someone completed a write after our shapshot
            if (r.CurrentValPoint() > _readPoint)
            {
                r.ExitReadLock();
                throw _retryex;
            }

            Info refinfo = r.TInfo;

            // writer exists
            if (refinfo != null && refinfo.IsRunning)
            {
                r.ExitReadLock();
                if (refinfo != _info)  // not us, ensure is doomed
                {
                    BlockAndBail(refinfo);
                }
            }
            else
            {
                _ensures.Add(r);
            }
        }
Ejemplo n.º 3
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);
        }