/// <summary>
 /// Gets how many result-box instances were allocated
 /// </summary>
 public static long GetResultBoxAllocationCount()
 {
     return(ResultBox.GetAllocationCount());
 }
示例#2
0
 internal ConditionResult(Condition condition)
 {
     this.Condition = condition;
     resultBox      = ResultBox <bool> .Get(condition);
 }
示例#3
0
 internal abstract IEnumerable <Message> CreateMessages(int db, ResultBox resultBox);
示例#4
0
 internal void SetSource <T>(ResultBox <T> resultBox, ResultProcessor <T> resultProcessor)
 {
     this.resultBox       = resultBox;
     this.resultProcessor = resultProcessor;
 }
示例#5
0
 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"
            }