/// <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); }
/// <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); }
private async Task <bool> ValidateUpgradeAsync(Task <bool> upgradeTask, TimeSpan?timeout) { var succeeded = await upgradeTask.ConfigureAwait(false); DistributedLockHelpers.ValidateTryAcquireResult(succeeded, timeout); return(succeeded); }
/// <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); } } }
private async Task <bool> ValidateUpgradeAsync(Task <bool> upgradeTask, TimeSpan?timeout) { if (!await upgradeTask.ConfigureAwait(false)) { throw DistributedLockHelpers.CreateTryAcquireFailedException(timeout); } return(true); }
/// <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); }
/// <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)); }
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); }
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);
static string ConvertToSafeSuffix(string suffix) => DistributedLockHelpers.ToSafeName( suffix, MaxNameLength - GlobalPrefix.Length, s => s.Length == 0 ? "EMPTY" : s.Replace('\\', '_') );
/// <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);
/// <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)); }
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);
/// <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);
/// <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))); }
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);
/// <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)); }
/// <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);
/// <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)); }
ZooKeeperDistributedReaderWriterLockHandle IInternalDistributedReaderWriterLock <ZooKeeperDistributedReaderWriterLockHandle> .AcquireWriteLock(TimeSpan?timeout, CancellationToken cancellationToken) => DistributedLockHelpers.Acquire(this, timeout, cancellationToken, isWrite: true);
/// <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);
/// <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);
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)); } }