Esempio n. 1
0
        private async ValueTask <WriteResult> WriteMessageTakingWriteLockAsync_Awaited(ValueTask <LockToken> pending, PhysicalConnection physical, Message message)
        {
            try
            {
                using (var token = await pending)
                {
                    if (!token.Success)
                    {
                        return(TimedOutBeforeWrite(message));
                    }

                    var result = WriteMessageInsideLock(physical, message);

                    if (result == WriteResult.Success)
                    {
                        result = await physical.FlushAsync(false);
                    }

                    UnmarkActiveMessage(message);
                    physical.SetIdle();

                    return(result);
                }
            }
            catch (Exception ex)
            {
                return(HandleWriteException(message, ex));
            }
        }
Esempio n. 2
0
        private async ValueTask <WriteResult> WriteMessageTakingDelayedWriteLockAsync(ValueTask <LockToken> pendingLock, PhysicalConnection physical, Message message)
        {
            try
            {
                // WriteMessageTakingWriteLockAsync will have checked for immediate availability,
                // so this is the fallback case - fine to go straight to "await"

                // note: timeout is specified in mutex-constructor
                using (var token = await pendingLock)
                {
                    if (!token.Success)
                    {
                        message.Cancel();
                        Multiplexer?.OnMessageFaulted(message, null);
                        this.CompleteSyncOrAsync(message);
                        return(WriteResult.TimeoutBeforeWrite);
                    }

                    var result = WriteMessageInsideLock(physical, message);

                    if (result == WriteResult.Success)
                    {
                        result = await physical.FlushAsync(false).ForAwait();
                    }

                    physical.SetIdle();
                    UnmarkActiveMessage(message);
                    return(result);
                }
            }
            catch (Exception ex) { return(HandleWriteException(message, ex)); }
        }
Esempio n. 3
0
        private async Task <WriteResult> WriteMessageTakingDelayedWriteLockAsync(PhysicalConnection physical, Message message)
        {
            bool haveLock = false;

            try
            {
                // WriteMessageTakingWriteLockAsync will have checked for immediate availability,
                // so this is the fallback case - fine to go straight to "await"
                haveLock = await _SingleWriterLock.WaitAsync(TimeoutMilliseconds).ForAwait();

                if (!haveLock)
                {
                    message.Cancel();
                    Multiplexer?.OnMessageFaulted(message, null);
                    this.CompleteSyncOrAsync(message);
                    return(WriteResult.TimeoutBeforeWrite);
                }

                var result = WriteMessageInsideLock(physical, message);

                if (result == WriteResult.Success)
                {
                    result = await physical.FlushAsync(false).ForAwait();
                }

                physical.SetIdle();
                return(result);
            }
            catch (Exception ex) { return(HandleWriteException(message, ex)); }
            finally { if (haveLock)
                      {
                          ReleaseSingleWriterLock(message);
                      }
            }
        }
Esempio n. 4
0
        /// <summary>
        /// This writes a message to the output stream
        /// </summary>
        /// <param name="physical">The phsyical connection to write to.</param>
        /// <param name="message">The message to be written.</param>
        internal ValueTask <WriteResult> WriteMessageTakingWriteLockAsync(PhysicalConnection physical, Message message)
        {
            Trace("Writing: " + message);
            message.SetEnqueued(physical); // this also records the read/write stats at this point

            bool      releaseLock = false;
            LockToken token       = default;

            try
            {
                // try to acquire it synchronously
                // note: timeout is specified in mutex-constructor
                var pending = _singleWriterMutex.TryWaitAsync(options: MutexSlim.WaitOptions.DisableAsyncContext);
                if (!pending.IsCompletedSuccessfully)
                {
                    return(WriteMessageTakingDelayedWriteLockAsync(pending, physical, message));
                }

                releaseLock = true;
                token       = pending.Result; // we can't use "using" for this, because we might not want to kill it yet
                if (!token.Success)           // (in particular, me might hand the lifetime to CompleteWriteAndReleaseLockAsync)
                {
                    message.Cancel();
                    Multiplexer?.OnMessageFaulted(message, null);
                    this.CompleteSyncOrAsync(message);
                    return(new ValueTask <WriteResult>(WriteResult.TimeoutBeforeWrite));
                }

                var result = WriteMessageInsideLock(physical, message);

                if (result == WriteResult.Success)
                {
                    var flush = physical.FlushAsync(false);
                    if (!flush.IsCompletedSuccessfully)
                    {
                        releaseLock = false; // so we don't release prematurely
                        return(CompleteWriteAndReleaseLockAsync(token, flush, message));
                    }

                    result = flush.Result; // we know it was completed, this is fine
                }

                UnmarkActiveMessage(message);
                physical.SetIdle();

                return(new ValueTask <WriteResult>(result));
            }
            catch (Exception ex) { return(new ValueTask <WriteResult>(HandleWriteException(message, ex))); }
            finally
            {
                if (releaseLock)
                {
                    token.Dispose();
                }
            }
        }
        /// <summary>
        /// This writes a message to the output stream
        /// </summary>
        /// <param name="physical">The phsyical connection to write to.</param>
        /// <param name="message">The message to be written.</param>
        internal ValueTask <WriteResult> WriteMessageTakingWriteLockAsync(PhysicalConnection physical, Message message)
        {
            Trace("Writing: " + message);
            message.SetEnqueued(physical); // this also records the read/write stats at this point

            bool      releaseLock = true;
            LockToken token       = default;

            try
            {
                // try to acquire it synchronously
                // note: timeout is specified in mutex-constructor
                token = _singleWriterMutex.TryWait(options: WaitOptions.NoDelay);

                if (!token.Success) // (in particular, me might hand the lifetime to CompleteWriteAndReleaseLockAsync)
                {
                    PushToBacklog(message);
                    return(new ValueTask <WriteResult>(WriteResult.Success)); // queued counts as success
                }

                var result = WriteMessageInsideLock(physical, message);

                if (result == WriteResult.Success)
                {
                    var flush = physical.FlushAsync(false);
                    if (!flush.IsCompletedSuccessfully)
                    {
                        releaseLock = false; // so we don't release prematurely
                        return(CompleteWriteAndReleaseLockAsync(token, flush, message));
                    }

                    result = flush.Result; // we know it was completed, this is fine
                }

                UnmarkActiveMessage(message);
                physical.SetIdle();

                return(new ValueTask <WriteResult>(result));
            }
            catch (Exception ex) { return(new ValueTask <WriteResult>(HandleWriteException(message, ex))); }
            finally
            {
                if (releaseLock)
                {
                    token.Dispose();
                }
            }
        }
Esempio n. 6
0
        /// <summary>
        /// This writes a message to the output stream
        /// </summary>
        /// <param name="physical">The phsyical connection to write to.</param>
        /// <param name="message">The message to be written.</param>
        internal ValueTask <WriteResult> WriteMessageTakingWriteLockAsync(PhysicalConnection physical, Message message)
        {
            Trace("Writing: " + message);
            message.SetEnqueued(physical); // this also records the read/write stats at this point

            bool haveLock = false;

            try
            {
                // try to acquire it synchronously
                haveLock = _SingleWriterLock.Wait(0);
                if (!haveLock)
                {
                    return(new ValueTask <WriteResult>(WriteMessageTakingDelayedWriteLockAsync(physical, message)));
                }

                var result = WriteMessageInsideLock(physical, message);

                if (result == WriteResult.Success)
                {
                    var flush = physical.FlushAsync(false);
                    if (!flush.IsCompletedSuccessfully)
                    {
                        haveLock = false; // so we don't release prematurely
                        return(new ValueTask <WriteResult>(CompleteWriteAndReleaseLockAsync(flush, message)));
                    }

                    result = flush.Result; // we know it was completed, this is fine
                }
                physical.SetIdle();

                return(new ValueTask <WriteResult>(result));
            }
            catch (Exception ex) { return(new ValueTask <WriteResult>(HandleWriteException(message, ex))); }
            finally { if (haveLock)
                      {
                          ReleaseSingleWriterLock(message);
                      }
            }
        }
        private Task HandshakeAsync(PhysicalConnection connection, TextWriter log)
        {
            Multiplexer.LogLocked(log, "Server handshake");
            if (connection == null)
            {
                Multiplexer.Trace("No connection!?");
                return(Task.CompletedTask);
            }
            Message msg;
            string  password = Multiplexer.RawConfig.Password;

            if (!string.IsNullOrWhiteSpace(password))
            {
                Multiplexer.LogLocked(log, "Authenticating (password)");
                msg = Message.Create(-1, CommandFlags.FireAndForget, RedisCommand.AUTH, (RedisValue)password);
                msg.SetInternalCall();
                WriteDirectOrQueueFireAndForget(connection, msg, ResultProcessor.DemandOK);
            }
            if (Multiplexer.CommandMap.IsAvailable(RedisCommand.CLIENT))
            {
                string name = Multiplexer.ClientName;
                if (!string.IsNullOrWhiteSpace(name))
                {
                    name = nameSanitizer.Replace(name, "");
                    if (!string.IsNullOrWhiteSpace(name))
                    {
                        Multiplexer.LogLocked(log, "Setting client name: {0}", name);
                        msg = Message.Create(-1, CommandFlags.FireAndForget, RedisCommand.CLIENT, RedisLiterals.SETNAME, (RedisValue)name);
                        msg.SetInternalCall();
                        WriteDirectOrQueueFireAndForget(connection, msg, ResultProcessor.DemandOK);
                    }
                }
            }

            var bridge = connection.BridgeCouldBeNull;

            if (bridge == null)
            {
                return(Task.CompletedTask);
            }
            var connType = bridge.ConnectionType;

            if (connType == ConnectionType.Interactive)
            {
                Multiplexer.LogLocked(log, "Auto-configure...");
                AutoConfigure(connection);
            }
            Multiplexer.LogLocked(log, "Sending critical tracer: {0}", bridge);
            var tracer = GetTracerMessage(true);

            tracer = LoggingMessage.Create(log, tracer);
            WriteDirectOrQueueFireAndForget(connection, tracer, ResultProcessor.EstablishConnection);

            // note: this **must** be the last thing on the subscription handshake, because after this
            // we will be in subscriber mode: regular commands cannot be sent
            if (connType == ConnectionType.Subscription)
            {
                var configChannel = Multiplexer.ConfigurationChangedChannel;
                if (configChannel != null)
                {
                    msg = Message.Create(-1, CommandFlags.FireAndForget, RedisCommand.SUBSCRIBE, (RedisChannel)configChannel);
                    WriteDirectOrQueueFireAndForget(connection, msg, ResultProcessor.TrackSubscriptions);
                }
            }
            Multiplexer.LogLocked(log, "Flushing outbound buffer");
            return(connection.FlushAsync());
        }
Esempio n. 8
0
        /// <summary>
        /// This writes a message to the output stream
        /// </summary>
        /// <param name="physical">The phsyical connection to write to.</param>
        /// <param name="message">The message to be written.</param>
        internal ValueTask <WriteResult> WriteMessageTakingWriteLockAsync(PhysicalConnection physical, Message message)
        {
            /* design decision/choice; the code works fine either way, but if this is
             * set to *true*, then when we can't take the writer-lock *right away*,
             * we push the message to the backlog (starting a worker if needed)
             *
             * otherwise, we go for a TryWaitAsync and rely on the await machinery
             *
             * "true" seems to give faster times *when under heavy contention*, based on profiling
             * but it involves the backlog concept; "false" works well under low contention, and
             * makes more use of async
             */
            const bool ALWAYS_USE_BACKLOG_IF_CANNOT_GET_SYNC_LOCK = true;

            Trace("Writing: " + message);
            message.SetEnqueued(physical); // this also records the read/write stats at this point

            bool      releaseLock = true;  // fine to default to true, as it doesn't matter until token is a "success"
            LockToken token       = default;

            try
            {
                // try to acquire it synchronously
                // note: timeout is specified in mutex-constructor
                token = _singleWriterMutex.TryWait(options: WaitOptions.NoDelay);

                if (!token.Success)
                {
                    // we can't get it *instantaneously*; is there
                    // perhaps a backlog and active backlog processor?
                    if (PushToBacklog(message, onlyIfExists: !ALWAYS_USE_BACKLOG_IF_CANNOT_GET_SYNC_LOCK))
                    {
                        return(new ValueTask <WriteResult>(WriteResult.Success)); // queued counts as success
                    }
                    // no backlog... try to wait with the timeout;
                    // if we *still* can't get it: that counts as
                    // an actual timeout
                    var pending = _singleWriterMutex.TryWaitAsync(options: WaitOptions.DisableAsyncContext);
                    if (!pending.IsCompletedSuccessfully)
                    {
                        return(WriteMessageTakingWriteLockAsync_Awaited(pending, physical, message));
                    }

                    token = pending.Result; // fine since we know we got a result
                    if (!token.Success)
                    {
                        return(new ValueTask <WriteResult>(TimedOutBeforeWrite(message)));
                    }
                }

                var result = WriteMessageInsideLock(physical, message);

                if (result == WriteResult.Success)
                {
                    var flush = physical.FlushAsync(false);
                    if (!flush.IsCompletedSuccessfully)
                    {
                        releaseLock = false; // so we don't release prematurely
                        return(CompleteWriteAndReleaseLockAsync(token, flush, message));
                    }

                    result = flush.Result; // we know it was completed, this is fine
                }

                UnmarkActiveMessage(message);
                physical.SetIdle();

                return(new ValueTask <WriteResult>(result));
            }
            catch (Exception ex) { return(new ValueTask <WriteResult>(HandleWriteException(message, ex))); }
            finally
            {
                if (releaseLock)
                {
                    token.Dispose();
                }
            }
        }