/// <summary> /// Gets how many result-box instances were allocated /// </summary> public static long GetResultBoxAllocationCount() { return(ResultBox.GetAllocationCount()); }
internal ConditionResult(Condition condition) { this.Condition = condition; resultBox = ResultBox <bool> .Get(condition); }
internal abstract IEnumerable <Message> CreateMessages(int db, ResultBox resultBox);
internal void SetSource <T>(ResultBox <T> resultBox, ResultProcessor <T> resultProcessor) { this.resultBox = resultBox; this.resultProcessor = resultProcessor; }
internal void SetSource(ResultProcessor resultProcessor, ResultBox resultBox) { // note order here reversed to prevent overload resolution errors this.resultBox = resultBox; this.resultProcessor = resultProcessor; }
public IEnumerable <Message> GetMessages(PhysicalConnection connection) { ResultBox lastBox = null; try { // Important: if the server supports EXECABORT, then we can check the pre-conditions (pause there), // which will usually be pretty small and cheap to do - if that passes, we can just isue all the commands // and rely on EXECABORT to kick us if we are being idiotic inside the MULTI. However, if the server does // *not* support EXECABORT, then we need to explicitly check for QUEUED anyway; we might as well defer // checking the preconditions to the same time to avoid having to pause twice. This will mean that on // up-version servers, pre-condition failures exit with UNWATCH; and on down-version servers pre-condition // failures exit with DISCARD - but that's ok : both work fine bool explicitCheckForQueued = !connection.Bridge.ServerEndPoint.GetFeatures().ExecAbort; var multiplexer = connection.Multiplexer; // PART 1: issue the pre-conditions if (!IsAborted && conditions.Length != 0) { for (int i = 0; i < conditions.Length; i++) { // need to have locked them before sending them // to guarantee that we see the pulse ResultBox latestBox = conditions[i].GetBox(); Monitor.Enter(latestBox); if (lastBox != null) { Monitor.Exit(lastBox); } lastBox = latestBox; foreach (var msg in conditions[i].CreateMessages(Db)) { msg.SetNoRedirect(); // need to keep them in the current context only yield return(msg); } } if (!explicitCheckForQueued && lastBox != null) { // need to get those sent ASAP; if they are stuck in the buffers, we die multiplexer.Trace("Flushing and waiting for precondition responses"); connection.Flush(); if (Monitor.Wait(lastBox, multiplexer.TimeoutMilliseconds)) { if (!AreAllConditionsSatisfied(multiplexer)) { command = RedisCommand.UNWATCH; // somebody isn't happy } } else { // timeout running pre-conditions multiplexer.Trace("Timeout checking preconditions"); command = RedisCommand.UNWATCH; } Monitor.Exit(lastBox); lastBox = null; } } // PART 2: begin the transaction if (!IsAborted) { multiplexer.Trace("Begining transaction"); yield return(Message.Create(-1, CommandFlags.None, RedisCommand.MULTI)); } // PART 3: issue the commands if (!IsAborted && operations.Length != 0) { multiplexer.Trace("Issuing transaction operations"); foreach (var op in operations) { if (explicitCheckForQueued) { // need to have locked them before sending them // to guarantee that we see the pulse ResultBox thisBox = op.ResultBox; if (thisBox != null) { Monitor.Enter(thisBox); if (lastBox != null) { Monitor.Exit(lastBox); } lastBox = thisBox; } } yield return(op); } if (explicitCheckForQueued && lastBox != null) { multiplexer.Trace("Flushing and waiting for precondition+queued responses"); connection.Flush(); // make sure they get sent, so we can check for QUEUED (and the pre-conditions if necessary) if (Monitor.Wait(lastBox, multiplexer.TimeoutMilliseconds)) { if (!AreAllConditionsSatisfied(multiplexer)) { command = RedisCommand.DISCARD; } else { foreach (var op in operations) { if (!op.WasQueued) { multiplexer.Trace("Aborting: operation was not queued: " + op.Command); command = RedisCommand.DISCARD; break; } } } multiplexer.Trace("Confirmed: QUEUED x " + operations.Length); } else { multiplexer.Trace("Aborting: timeout checking queued messages"); command = RedisCommand.DISCARD; } Monitor.Exit(lastBox); lastBox = null; } } } finally { if (lastBox != null) { Monitor.Exit(lastBox); } } if (IsAborted) { connection.Multiplexer.Trace("Aborting: canceling wrapped messages"); var bridge = connection.Bridge; foreach (var op in operations) { op.Wrapped.Cancel(); bridge.CompleteSyncOrAsync(op.Wrapped); } } connection.Multiplexer.Trace("End ot transaction: " + Command); yield return(this); // acts as either an EXEC or an UNWATCH, depending on "aborted" }