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); }
public Notify(Ref r, object oldval, object newval) { _ref = r; _oldval = oldval; _newval = newval; }