/// <summary> /// Wait for writer to finish. /// </summary> /// <param name="me">caller's XState object</param> /// <param name="writer">prior writer's XState object</param> public void ResolveConflict(XState me, XState writer) { while (writer.State == XStates.ACTIVE) { writer.BlockWhileActiveAndNotWaiting(); } }
public static object Run(XStart start, params object[] args) { object result = null; depth++; // less confusing if higher number is higher priority long priority = Manager.Priority; while (!XAction.stop) { xState = new XState(priority); try { result = start(args); // user's delegate depth--; } catch (AbortedException e) // aborted by synch conflict, retry { if (depth > 1) { depth--; throw e; } } catch (RetryException e) // user wants to retry { if (depth > 1) { depth--; throw e; } // block until another transctions writes something I've read (aborting me) xState.BlockWhileActive(); } depth--; if (depth > 0) { return(result); } else { if (xState.Commit()) { XAction.myCommits++; return(result); } else { XAction.myAborts++; } } } UpdateStatistic(ref commits, myCommits); UpdateStatistic(ref aborts, myAborts); throw new GracefulException(); }
/// <summary> /// Either give the writer a chance to finish it, abort it, or both. /// </summary> /// <param name="me">caller's XState object</param> /// <param name="writer">prior writer's XState object</param> public void ResolveConflict(XState me, XState writer) { if (writer.priority > me.priority) { writer.BlockWhileActiveAndNotWaiting(); // wait for same or higher priority } else { writer.Abort(); // abort lower priority } }
bool ConflictsWithReader(XState me, ICollection readers) { foreach (XState reader in readers) { if (reader.State == XStates.ACTIVE && reader != me) { return(true); } } return(false); }
/// <summary> /// If prior transaction is /// active and higher priority, then wait. /// active and lower priority, then abort it. /// waiting and higher priority, then adopt its priority and abort it. /// waiting and lower priority, then abort it. /// </summary> /// <param name="me">caller's XState object</param> /// <param name="writer">prior writer's XState object</param> public void ResolveConflict(XState me, XState writer) { if (writer.waiting || writer.priority < me.priority) { writer.Abort(); } else { me.Waiting(); // announce that I'm waiting writer.BlockWhileActiveAndNotWaiting(); // wait for prior transaction to become not ACTIVE me.NotWaiting(); // announce that I'm no longer waiting } }
/// <summary> /// If exponential backoff fails, then abort the writer. /// </summary> /// <param name="me">caller's XState object</param> /// <param name="writer">prior writer's XState object</param> public void ResolveConflict(XState me, XState writer) { int sleep = random.Next(1 << attempts); Thread.Sleep(sleep); if (writer.State != XStates.ACTIVE) { attempts = 0; } else { writer.Abort(); // no more Mister Nice Guy. if (attempts < MAX_LOG_BACKOFF) { attempts++; } } return; }
/// <summary> /// Principal constructor. /// </summary> public Locator() { this.writer = XState.COMMITTED; this.newObject = null; this.oldObject = null; }
/// <summary> /// Immediately abort the writer. /// </summary> /// <param name="me">caller's XState object</param> /// <param name="writer">prior writer's XState object</param> public void ResolveConflict(XState me, XState writer) { writer.Abort(); }
/// <summary> /// Open object with intention to modify. /// </summary> /// <returns>Private version of object</returns> public ICloneable OpenWrite() { XState me = XAction.XState; // my transaction Locator oldLocator = (Locator)this.start; XState writer = oldLocator.writer; if (writer == me) { return(oldLocator.newObject); } IContentionManager manager = XAction.Manager; // my manager // allocate successor Locator newLocator = new Locator(); newLocator.writer = me; ICloneable oldVersion = null; ICloneable newVersion = null; while (true) { retry: // read locator switch (writer.State) { case XStates.ACTIVE: // abort or wait? manager.ResolveConflict(me, writer); goto retry; // try again case XStates.COMMITTED: oldVersion = newLocator.oldObject = oldLocator.newObject; break; case XStates.ABORTED: oldVersion = newLocator.oldObject = oldLocator.oldObject; break; default: throw new PanicException("Unexpected transaction state: {0}", writer.State); } switch (me.State) { case XStates.ABORTED: throw new AbortedException(); case XStates.COMMITTED: return(oldVersion); case XStates.ACTIVE: // check for read conflicts bool readConflict = false; foreach (XState reader in oldLocator) { if (reader.State == XStates.ACTIVE && reader != me) { manager.ResolveConflict(me, reader); readConflict = true; } if (readConflict) { goto retry; } } // no conflict newVersion = newLocator.newObject = (ICloneable)oldVersion.Clone(); // try to install if ((Locator)(Interlocked.CompareExchange( ref start, newLocator, oldLocator)) == oldLocator) { return(newVersion); } break; default: throw new PanicException("Unknown transaction state: {0}", me.State); } oldLocator = (Locator)this.start; writer = oldLocator.writer; } }
/// <summary> /// Open object with intention to read. /// </summary> /// <returns>Shared version of object</returns> public ICloneable OpenRead() { XState me = XAction.XState; // my transaction Locator oldLocator = (Locator)this.start; XState writer = oldLocator.writer; if (writer == me) { return(oldLocator.newObject); } foreach (XState reader in oldLocator) { if (reader == me) { return(oldLocator.newObject); } } IContentionManager manager = XAction.Manager; // my manager // allocate successor Locator newLocator = new Locator(); newLocator.writer = XState.COMMITTED; while (true) { retry: // read locator ICloneable version = null; switch (writer.State) { case XStates.ACTIVE: // abort or wait? manager.ResolveConflict(me, writer); goto retry; // try again case XStates.COMMITTED: version = oldLocator.newObject; break; case XStates.ABORTED: version = oldLocator.oldObject; break; default: throw new PanicException("Unknown transaction state: {0}", writer.State); } switch (me.State) { case XStates.ABORTED: throw new AbortedException(); case XStates.COMMITTED: return(version); case XStates.ACTIVE: newLocator.newObject = version; newLocator.reader = me; if (oldLocator.reader != null) { newLocator.prevReader = oldLocator; } else { newLocator.prevReader = null; } if (Interlocked.CompareExchange( ref start, newLocator, oldLocator) == oldLocator) { return(version); } break; default: throw new PanicException("Unknown transaction state: {0}", me.State); } oldLocator = (Locator)this.start; writer = oldLocator.writer; } }
/// <summary> /// Initialize XStatus constant. /// </summary> /// <param name="init">must be one of static state objects</param> private XStatus(XState init) { state = (int)init; // convenient to assign highest priority to constants priority = Int64.MaxValue; }