コード例 #1
0
        /// <summary>
        /// Acquires a WRITE lock synchronously. Not compatible with another WRITE lock or a UPGRADE lock
        /// </summary>
        public IDisposable AcquireWriteLock(TimeSpan?timeout = null, CancellationToken cancellationToken = default(CancellationToken))
        {
            var handle = this.TryAcquireWriteLock(timeout ?? Timeout.InfiniteTimeSpan, cancellationToken);

            DistributedLockHelpers.ValidateTryAcquireResult(handle, timeout);
            return(handle);
        }
コード例 #2
0
            /// <summary>
            /// Upgrades to a WRITE lock synchronously. Not compatible with another WRITE lock or a UPGRADE lock
            /// </summary>
            public bool UpgradeToWriteLock(TimeSpan?timeout = null, CancellationToken cancellationToken = default(CancellationToken))
            {
                var succeeded = this.TryUpgradeToWriteLock(timeout ?? Timeout.InfiniteTimeSpan, cancellationToken);

                DistributedLockHelpers.ValidateTryAcquireResult(succeeded, timeout);
                return(succeeded);
            }
コード例 #3
0
            private async Task <bool> ValidateUpgradeAsync(Task <bool> upgradeTask, TimeSpan?timeout)
            {
                var succeeded = await upgradeTask.ConfigureAwait(false);

                DistributedLockHelpers.ValidateTryAcquireResult(succeeded, timeout);
                return(succeeded);
            }
コード例 #4
0
        /// <summary>
        /// Attempts to acquire the lock synchronously. Usage:
        /// <code>
        ///     using (var handle = myLock.TryAcquire(...))
        ///     {
        ///         if (handle != null) { /* we have the lock! */ }
        ///     }
        ///     // dispose releases the lock if we took it
        /// </code>
        /// </summary>
        /// <param name="timeout">How long to wait before giving up on acquiring the lock. Defaults to 0</param>
        /// <param name="cancellationToken">Specifies a token by which the wait can be canceled</param>
        /// <returns>An <see cref="IDisposable"/> "handle" which can be used to release the lock, or null if the lock was not taken</returns>
        public IDisposable TryAcquire(TimeSpan timeout = default(TimeSpan), CancellationToken cancellationToken = default(CancellationToken))
        {
            if (cancellationToken.CanBeCanceled)
            {
                // use the async version since that supports cancellation
                return(DistributedLockHelpers.TryAcquireWithAsyncCancellation(this, timeout, cancellationToken));
            }

            // synchronous mode
            var timeoutMillis = timeout.ToInt32Timeout();

            DbConnection  connection  = null;
            DbTransaction transaction = null;
            var           cleanup     = true;

            try
            {
                connection = this.GetConnection();
                if (this.connectionString != null)
                {
                    connection.Open();
                }
                else if (connection == null)
                {
                    throw new InvalidOperationException("The transaction had been disposed");
                }
                else if (connection.State != ConnectionState.Open)
                {
                    throw new InvalidOperationException("The connection is not open");
                }

                transaction = this.GetTransaction(connection);
                SqlParameter returnValue;
                using (var command = CreateAcquireCommand(connection, transaction, this.lockName, timeoutMillis, out returnValue))
                {
                    command.ExecuteNonQuery();
                    var exitCode = (int)returnValue.Value;
                    if (ParseExitCode(exitCode))
                    {
                        cleanup = false;
                        return(new LockScope(this, transaction));
                    }
                    return(null);
                }
            }
            catch
            {
                // in case we fail to create lock scope or something
                cleanup = true;
                throw;
            }
            finally
            {
                if (cleanup)
                {
                    this.Cleanup(transaction, connection);
                }
            }
        }
コード例 #5
0
 private async Task <bool> ValidateUpgradeAsync(Task <bool> upgradeTask, TimeSpan?timeout)
 {
     if (!await upgradeTask.ConfigureAwait(false))
     {
         throw DistributedLockHelpers.CreateTryAcquireFailedException(timeout);
     }
     return(true);
 }
コード例 #6
0
 /// <summary>
 /// Upgrades to a WRITE lock synchronously. Not compatible with another WRITE lock or a UPGRADE lock
 /// </summary>
 public bool UpgradeToWriteLock(TimeSpan?timeout = null, CancellationToken cancellationToken = default)
 {
     if (!this.TryUpgradeToWriteLock(timeout ?? Timeout.InfiniteTimeSpan, cancellationToken))
     {
         throw DistributedLockHelpers.CreateTryAcquireFailedException(timeout);
     }
     return(true);
 }
コード例 #7
0
 /// <summary>
 /// Attempts to acquire the lock synchronously. Usage:
 /// <code>
 ///     using (var handle = myLock.TryAcquire(...))
 ///     {
 ///         if (handle != null) { /* we have the lock! */ }
 ///     }
 ///     // dispose releases the lock if we took it
 /// </code>
 /// </summary>
 /// <param name="timeout">How long to wait before giving up on acquiring the lock. Defaults to 0</param>
 /// <param name="cancellationToken">Specifies a token by which the wait can be canceled</param>
 /// <returns>An <see cref="IDisposable"/> "handle" which can be used to release the lock, or null if the lock was not taken</returns>
 public IDisposable TryAcquire(TimeSpan timeout = default(TimeSpan), CancellationToken cancellationToken = default(CancellationToken))
 {
     return(cancellationToken.CanBeCanceled
            // use the async version since that supports cancellation
         ? DistributedLockHelpers.TryAcquireWithAsyncCancellation(this, timeout, cancellationToken)
            // synchronous mode
         : this.internalLock.TryAcquire(timeout.ToInt32Timeout(), SqlApplicationLock.Mode.Exclusive, contextHandle: null));
 }
コード例 #8
0
        public string SetUniqueApplicationName(string baseName = "")
        {
            // note: due to retries, we incorporate a GUID here to ensure that we have a fresh connection pool
            var applicationName = DistributedLockHelpers.ToSafeName(
                $"{(baseName.Length > 0 ? baseName + "_" : string.Empty)}{TestContext.CurrentContext.Test.FullName}_{TargetFramework.Current}_{Guid.NewGuid()}",
                maxNameLength: this.Db.MaxApplicationNameLength,
                s => s
                );

            this.Db.ConnectionStringBuilder["Application Name"] = applicationName;
            return(applicationName);
        }
コード例 #9
0
        internal static string GetName(string name, bool exactName)
        {
            if (name == null)
            {
                throw new ArgumentNullException(nameof(name));
            }

            if (exactName)
            {
                if (name.Length > MaxNameLength)
                {
                    throw new FormatException($"{nameof(name)}: must be at most {MaxNameLength} characters");
                }
                // Oracle treats NULL as the empty string. See https://stackoverflow.com/questions/13278773/null-vs-empty-string-in-oracle
                if (name.Length == 0)
                {
                    throw new FormatException($"{nameof(name)} must not be empty");
                }
                return(name);
            }

            return(DistributedLockHelpers.ToSafeName(name, MaxNameLength, s => s.Length == 0 ? "EMPTY" : s));
        }
 /// <summary>
 /// Acquires an UPGRADE lock synchronously, failing with <see cref="TimeoutException"/> if the attempt times out. Not compatible with another UPGRADE lock or a WRITE lock. Usage:
 /// <code>
 ///     using (myLock.AcquireUpgradeableReadLock(...))
 ///     {
 ///         /* we have the lock! */
 ///     }
 ///     // dispose releases the lock
 /// </code>
 /// </summary>
 /// <param name="timeout">How long to wait before giving up on the acquisition attempt. Defaults to <see cref="Timeout.InfiniteTimeSpan"/></param>
 /// <param name="cancellationToken">Specifies a token by which the wait can be canceled</param>
 /// <returns>An <see cref="OracleDistributedReaderWriterLockUpgradeableHandle"/> which can be used to release the lock</returns>
 public OracleDistributedReaderWriterLockUpgradeableHandle AcquireUpgradeableReadLock(TimeSpan?timeout = null, CancellationToken cancellationToken = default) =>
 DistributedLockHelpers.AcquireUpgradeableReadLock(this, timeout, cancellationToken);
コード例 #11
0
 static string ConvertToSafeSuffix(string suffix) => DistributedLockHelpers.ToSafeName(
     suffix,
     MaxNameLength - GlobalPrefix.Length,
     s => s.Length == 0 ? "EMPTY" : s.Replace('\\', '_')
     );
コード例 #12
0
        /// <summary>
        /// Acquires a WRITE lock asynchronously. Not compatible with another WRITE lock or a UPGRADE lock
        /// </summary>
        public Task <IDisposable> AcquireWriteLockAsync(TimeSpan?timeout = null, CancellationToken cancellationToken = default(CancellationToken))
        {
            var handleTask = this.TryAcquireWriteLockAsync(timeout ?? Timeout.InfiniteTimeSpan, cancellationToken);

            return(DistributedLockHelpers.ValidateTryAcquireResultAsync(handleTask, timeout));
        }
 /// <summary>
 /// Acquires a WRITE lock asynchronously, failing with <see cref="TimeoutException"/> if the attempt times out. Not compatible with another WRITE lock or an UPGRADE lock. Usage:
 /// <code>
 ///     await using (await myLock.AcquireWriteLockAsync(...))
 ///     {
 ///         /* we have the lock! */
 ///     }
 ///     // dispose releases the lock
 /// </code>
 /// </summary>
 /// <param name="timeout">How long to wait before giving up on the acquisition attempt. Defaults to <see cref="Timeout.InfiniteTimeSpan"/></param>
 /// <param name="cancellationToken">Specifies a token by which the wait can be canceled</param>
 /// <returns>An <see cref="OracleDistributedReaderWriterLockHandle"/> which can be used to release the lock</returns>
 public ValueTask <OracleDistributedReaderWriterLockHandle> AcquireWriteLockAsync(TimeSpan?timeout = null, CancellationToken cancellationToken = default) =>
 DistributedLockHelpers.AcquireAsync(this, timeout, cancellationToken, isWrite: true);
 /// <summary>
 /// Acquires an UPGRADE lock asynchronously, failing with <see cref="TimeoutException"/> if the attempt times out. Not compatible with another UPGRADE lock or a WRITE lock. Usage:
 /// <code>
 ///     await using (await myLock.AcquireUpgradeableReadLockAsync(...))
 ///     {
 ///         /* we have the lock! */
 ///     }
 ///     // dispose releases the lock
 /// </code>
 /// </summary>
 /// <param name="timeout">How long to wait before giving up on the acquisition attempt. Defaults to <see cref="Timeout.InfiniteTimeSpan"/></param>
 /// <param name="cancellationToken">Specifies a token by which the wait can be canceled</param>
 /// <returns>A <see cref="SqlDistributedReaderWriterLockUpgradeableHandle"/> which can be used to release the lock</returns>
 public ValueTask <SqlDistributedReaderWriterLockUpgradeableHandle> AcquireUpgradeableReadLockAsync(TimeSpan?timeout = null, CancellationToken cancellationToken = default) =>
 DistributedLockHelpers.AcquireUpgradeableReadLockAsync(this, timeout, cancellationToken);
コード例 #15
0
 /// <summary>
 /// Acquires a semaphore ticket synchronously, failing with <see cref="TimeoutException"/> if the wait times out.
 /// <code>
 ///     using (mySemaphore.Acquire(...))
 ///     {
 ///         // success!
 ///     }
 ///     // dispose releases the ticket
 /// </code>
 /// </summary>
 /// <param name="timeout">How long to wait before giving up on acquiring the ticket. Defaults to <see cref="Timeout.InfiniteTimeSpan"/></param>
 /// <param name="cancellationToken">Specifies a token by which the wait can be canceled</param>
 /// <returns>An <see cref="IDisposable"/> "handle" which can be used to release the lock</returns>
 public IDisposable Acquire(TimeSpan?timeout = null, CancellationToken cancellationToken = default)
 {
     return(DistributedLockHelpers.Acquire(this, timeout, cancellationToken));
 }
コード例 #16
0
 ZooKeeperDistributedReaderWriterLockHandle?IInternalDistributedReaderWriterLock <ZooKeeperDistributedReaderWriterLockHandle> .TryAcquireReadLock(TimeSpan timeout, CancellationToken cancellationToken) =>
 DistributedLockHelpers.TryAcquire(this, timeout, cancellationToken, isWrite: false);
 ZooKeeperDistributedSemaphoreHandle IInternalDistributedSemaphore <ZooKeeperDistributedSemaphoreHandle> .Acquire(TimeSpan?timeout, CancellationToken cancellationToken) =>
 DistributedLockHelpers.Acquire(this, timeout, cancellationToken);
コード例 #18
0
 /// <summary>
 /// Acquires the lock synchronously, failing with <see cref="TimeoutException"/> if the attempt times out. Usage:
 /// <code>
 ///     using (myLock.Acquire(...))
 ///     {
 ///         /* we have the lock! */
 ///     }
 ///     // dispose releases the lock
 /// </code>
 /// </summary>
 /// <param name="timeout">How long to wait before giving up on the acquisition attempt. Defaults to <see cref="Timeout.InfiniteTimeSpan"/></param>
 /// <param name="cancellationToken">Specifies a token by which the wait can be canceled</param>
 /// <returns>A <see cref="FileDistributedLockHandle"/> which can be used to release the lock</returns>
 public FileDistributedLockHandle Acquire(TimeSpan?timeout = null, CancellationToken cancellationToken = default) =>
 DistributedLockHelpers.Acquire(this, timeout, cancellationToken);
コード例 #19
0
 /// <summary>
 /// Acquires a semaphore ticket synchronously, failing with <see cref="TimeoutException"/> if the wait times out.
 /// <code>
 ///     using (await mySemaphore.AcquireAsync(...))
 ///     {
 ///         // success!
 ///     }
 ///     // dispose releases the ticket
 /// </code>
 /// </summary>
 /// <param name="timeout">How long to wait before giving up on acquiring the ticket. Defaults to <see cref="Timeout.InfiniteTimeSpan"/></param>
 /// <param name="cancellationToken">Specifies a token by which the wait can be canceled</param>
 /// <returns>An <see cref="IDisposable"/> "handle" which can be used to release the lock</returns>
 public AwaitableDisposable <IDisposable> AcquireAsync(TimeSpan?timeout = null, CancellationToken cancellationToken = default)
 {
     return(new AwaitableDisposable <IDisposable>(DistributedLockHelpers.AcquireAsync(this, timeout, cancellationToken)));
 }
コード例 #20
0
 ZooKeeperDistributedLockHandle?IInternalDistributedLock <ZooKeeperDistributedLockHandle> .TryAcquire(TimeSpan timeout, CancellationToken cancellationToken) =>
 DistributedLockHelpers.TryAcquire(this, timeout, cancellationToken);
 /// <summary>
 /// Acquires a READ lock synchronously, failing with <see cref="TimeoutException"/> if the attempt times out. Multiple readers are allowed. Not compatible with a WRITE lock. Usage:
 /// <code>
 ///     using (myLock.AcquireReadLock(...))
 ///     {
 ///         /* we have the lock! */
 ///     }
 ///     // dispose releases the lock
 /// </code>
 /// </summary>
 /// <param name="timeout">How long to wait before giving up on the acquisition attempt. Defaults to <see cref="Timeout.InfiniteTimeSpan"/></param>
 /// <param name="cancellationToken">Specifies a token by which the wait can be canceled</param>
 /// <returns>A <see cref="SqlDistributedReaderWriterLockHandle"/> which can be used to release the lock</returns>
 public SqlDistributedReaderWriterLockHandle AcquireReadLock(TimeSpan?timeout = null, CancellationToken cancellationToken = default) =>
 DistributedLockHelpers.Acquire(this, timeout, cancellationToken, isWrite: false);
 /// <summary>
 /// Attempts to acquire a WRITE lock synchronously. Not compatible with another WRITE lock or an UPGRADE lock. Usage:
 /// <code>
 ///     using (var handle = myLock.TryAcquireWriteLock(...))
 ///     {
 ///         if (handle != null) { /* we have the lock! */ }
 ///     }
 ///     // dispose releases the lock if we took it
 /// </code>
 /// </summary>
 /// <param name="timeout">How long to wait before giving up on the acquisition attempt. Defaults to 0</param>
 /// <param name="cancellationToken">Specifies a token by which the wait can be canceled</param>
 /// <returns>A <see cref="SqlDistributedReaderWriterLockHandle"/> which can be used to release the lock or null on failure</returns>
 public SqlDistributedReaderWriterLockHandle?TryAcquireWriteLock(TimeSpan timeout = default, CancellationToken cancellationToken = default) =>
 DistributedLockHelpers.TryAcquire(this, timeout, cancellationToken, isWrite: true);
コード例 #23
0
 /// <summary>
 /// Acquires the lock asynchronously, failing with <see cref="TimeoutException"/> if the wait times out
 /// <code>
 ///     using (await myLock.AcquireAsync(...))
 ///     {
 ///         // we have the lock
 ///     }
 ///     // dispose releases the lock
 /// </code>
 /// </summary>
 /// <param name="timeout">How long to wait before giving up on acquiring the lock. Defaults to <see cref="Timeout.InfiniteTimeSpan"/></param>
 /// <param name="cancellationToken">Specifies a token by which the wait can be canceled</param>
 /// <returns>An <see cref="IDisposable"/> "handle" which can be used to release the lock</returns>
 public Task <IDisposable> AcquireAsync(TimeSpan?timeout = null, CancellationToken cancellationToken = default(CancellationToken))
 {
     return(DistributedLockHelpers.AcquireAsync(this, timeout, cancellationToken));
 }
コード例 #24
0
 /// <summary>
 /// Acquires a READ lock asynchronously, failing with <see cref="TimeoutException"/> if the attempt times out. Multiple readers are allowed. Not compatible with a WRITE lock. Usage:
 /// <code>
 ///     await using (await myLock.AcquireReadLockAsync(...))
 ///     {
 ///         /* we have the lock! */
 ///     }
 ///     // dispose releases the lock
 /// </code>
 /// </summary>
 /// <param name="timeout">How long to wait before giving up on the acquisition attempt. Defaults to <see cref="Timeout.InfiniteTimeSpan"/></param>
 /// <param name="cancellationToken">Specifies a token by which the wait can be canceled</param>
 /// <returns>A <see cref="ZooKeeperDistributedReaderWriterLockHandle"/> which can be used to release the lock</returns>
 public ValueTask <ZooKeeperDistributedReaderWriterLockHandle> AcquireReadLockAsync(TimeSpan?timeout = null, CancellationToken cancellationToken = default) =>
 DistributedLockHelpers.AcquireAsync(this, timeout, cancellationToken, isWrite: false);
コード例 #25
0
 /// <summary>
 /// Given <paramref name="baseLockName"/>, constructs a lock name which is safe for use with <see cref="SqlDistributedLock"/>
 /// </summary>
 public static string GetSafeLockName(string baseLockName)
 {
     return(DistributedLockHelpers.ToSafeLockName(baseLockName, MaxLockNameLength, s => s));
 }
コード例 #26
0
 ZooKeeperDistributedReaderWriterLockHandle IInternalDistributedReaderWriterLock <ZooKeeperDistributedReaderWriterLockHandle> .AcquireWriteLock(TimeSpan?timeout, CancellationToken cancellationToken) =>
 DistributedLockHelpers.Acquire(this, timeout, cancellationToken, isWrite: true);
コード例 #27
0
 /// <summary>
 /// Attempts to acquire the lock synchronously. Usage:
 /// <code>
 ///     using (var handle = myLock.TryAcquire(...))
 ///     {
 ///         if (handle != null) { /* we have the lock! */ }
 ///     }
 ///     // dispose releases the lock if we took it
 /// </code>
 /// </summary>
 /// <param name="timeout">How long to wait before giving up on the acquisition attempt. Defaults to 0</param>
 /// <param name="cancellationToken">Specifies a token by which the wait can be canceled</param>
 /// <returns>A <see cref="FileDistributedLockHandle"/> which can be used to release the lock or null on failure</returns>
 public FileDistributedLockHandle?TryAcquire(TimeSpan timeout = default, CancellationToken cancellationToken = default) =>
 DistributedLockHelpers.TryAcquire(this, timeout, cancellationToken);
 /// <summary>
 /// Attempts to acquire an UPGRADE lock synchronously. Not compatible with another UPGRADE lock or a WRITE lock. Usage:
 /// <code>
 ///     using (var handle = myLock.TryAcquireUpgradeableReadLock(...))
 ///     {
 ///         if (handle != null) { /* we have the lock! */ }
 ///     }
 ///     // dispose releases the lock if we took it
 /// </code>
 /// </summary>
 /// <param name="timeout">How long to wait before giving up on the acquisition attempt. Defaults to 0</param>
 /// <param name="cancellationToken">Specifies a token by which the wait can be canceled</param>
 /// <returns>A <see cref="SqlDistributedReaderWriterLockUpgradeableHandle"/> which can be used to release the lock or null on failure</returns>
 public SqlDistributedReaderWriterLockUpgradeableHandle?TryAcquireUpgradeableReadLock(TimeSpan timeout = default, CancellationToken cancellationToken = default) =>
 DistributedLockHelpers.TryAcquireUpgradeableReadLock(this, timeout, cancellationToken);
コード例 #29
0
 /// <summary>
 /// Acquires the lock asynchronously, failing with <see cref="TimeoutException"/> if the attempt times out. Usage:
 /// <code>
 ///     await using (await myLock.AcquireAsync(...))
 ///     {
 ///         /* we have the lock! */
 ///     }
 ///     // dispose releases the lock
 /// </code>
 /// </summary>
 /// <param name="timeout">How long to wait before giving up on the acquisition attempt. Defaults to <see cref="Timeout.InfiniteTimeSpan"/></param>
 /// <param name="cancellationToken">Specifies a token by which the wait can be canceled</param>
 /// <returns>A <see cref="FileDistributedLockHandle"/> which can be used to release the lock</returns>
 public ValueTask <FileDistributedLockHandle> AcquireAsync(TimeSpan?timeout = null, CancellationToken cancellationToken = default) =>
 DistributedLockHelpers.AcquireAsync(this, timeout, cancellationToken);
コード例 #30
0
        internal ZooKeeperPath GetChildNodePathWithSafeName(string name)
        {
            if (name == null)
            {
                throw new ArgumentNullException(nameof(name));
            }

            var isRoot   = this == Root;
            var safeName = DistributedLockHelpers.ToSafeName(
                name,
                maxNameLength: int.MaxValue,     // no max
                convertToValidName: ConvertToValidNodeName
                )
                           // If ToSafeName adds a hash, it uses Base64 encoding which can include the separator character. We replace
                           // with '_' which is not in Base64 so that the output name remains safe without weakening the hash
                           .Replace(Separator, '_');

            return(new ZooKeeperPath((this == Root ? this._path : (this._path + Separator)) + safeName, checkPath: false));

            string ConvertToValidNodeName(string name)
            {
                // in order to be a valid node name:

                // must not be empty (special-case this because our generic conversion method will map empty to itself)
                if (name.Length == 0)
                {
                    return("EMPTY");
                }

                // must not be ., .., or (this this is root), the reserved path "zookeeper"
                // (see https://zookeeper.apache.org/doc/current/zookeeperProgrammers.html#ch_zkDataModel)
                switch (name)
                {
                case ".":
                case "..":
                case "zookeeper" when isRoot:
                    return(name + "_");

                default:
                    break;     // keep going
                }

                if (name.IndexOf(Separator) < 0 && // must not contain the path separator
                    !ValidatePath(Separator + name).HasValue)    // "/name" must be a valid path
                {
                    return(name);
                }

                var converted = name.ToCharArray();

                for (var i = 0; i < name.Length; ++i)
                {
                    switch (name[i])
                    {
                    // note: we don't have to replace '.' because it is only invalid if '.' or '..' is a full path
                    // segment. Since we'll be appending on a hash, that doesn't matter
                    case Separator:     // separator cannot appear in names, only in paths
                    case '\0':
                    case char @char when IsNonNullInvalidPathChar(@char):
                        converted[i] = '_';     // replace with placeholder

                        break;
                    }
                }

                return(new string(converted));
            }
        }