public async Task Release(SemaphoreContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } var db = multiplexer.GetDatabase(database); await db.SortedSetRemoveAsync(context.SemaphoreName, context.Identifier); await db.SortedSetRemoveAsync(context.CounterSet, context.Identifier); }
/// <summary> /// Attempts to get a spot in the semaphore without first holding a lock /// </summary> /// <param name="semaphoreName"></param> /// <param name="semaphoreCount"></param> /// <param name="timeout"></param> /// <param name="identifier"></param> /// <returns></returns> private async Task <(bool wasAcquired, SemaphoreContext ctx)> TryGetSemaphoreUnsafe( IDatabase db, string semaphoreName, long semaphoreCount, TimeSpan timeout, string identifier) { var counterSetName = semaphoreName + ":owner"; var counterName = semaphoreName + ":counter"; var now = DateTime.UtcNow; var currentScore = ScoreDateTime(now); // Anything before the cutoff score has now expired var cutOffScore = ScoreDateTime(now - timeout); // remove any entries that no longer have a valid semaphore _ = await db.SortedSetRemoveRangeByScoreAsync(semaphoreName, 0, cutOffScore); // propogate any removed entries to the owner set from the timer set _ = await db.SortedSetCombineAndStoreAsync( SetOperation.Intersect, counterSetName, new RedisKey[] { counterSetName, semaphoreName }, new double[] { 1, 0 }); // increment the counter and get the latest value var counter = await db.StringIncrementAsync(counterName); // add the current time to the timer set await db.SortedSetAddAsync(semaphoreName, identifier, currentScore); // add the current count to the owner set await db.SortedSetAddAsync(counterSetName, identifier, counter); // check our position in the list var rank = await db.SortedSetRankAsync(counterSetName, identifier); var ctx = new SemaphoreContext { SemaphoreName = semaphoreName, Identifier = identifier, CounterSet = counterSetName }; // our rank is low enough (lower is better) so the caller has a spot in the semaphore if (rank.HasValue && rank.Value < semaphoreCount) { return(true, ctx); } // we are too far up the semaphore list to be allowed access to the semaphore // release our handle to the semaphore and fail the call await Release(ctx); return(false, null); }
/// <summary> /// Extend the semaphore /// </summary> /// <param name="context"></param> /// <returns></returns> public async Task <bool> TryExtendSemaphore(SemaphoreContext context) { var db = multiplexer.GetDatabase(database); var currentScore = ScoreDateTime(DateTime.UtcNow); var wasAdded = await db.SortedSetAddAsync( context.SemaphoreName, context.Identifier, currentScore); // we still have the semaphore, and have successfully updated it if (!wasAdded) { return(true); } // else we added it because we lost the semaphore, so need to delete the just added version // and return false await Release(context); return(false); }