- Drop netstandard2.0, switch to multi targeting .NET 8.0 and .NET 4.8.1
- Add
KeyedSemaphoresDictionary
and use it as the default for the static API. This resolves the nasty footgun that was present by usingKeyedSemaphoresCollection
as default
- Target .NET Standard 2.0 again, even though this library is only tested against .NET Framework 4.8 and .NET 7. Use on other platforms at your own risk.
- Make the synchronous wait duration configurable.
- Slightly simplify the implementation so we only need one array lookup instead of two
The public API has not been modified, except for the constructor of KeyedSemaphoresCollection
The internal key bookkeeping mechanism has been heavily refactored.
Internally, keys now map to an entry in an array of SemaphoreSlims.
This has the following pros and cons:
There is no more dynamic creation and disposal of semaphores. This avoids subtle race conditions, and drastically speeds up typical usage and reduces allocation to almost zero. The only real allocation happens when a semaphore is asynchronously awaited. Furthermore, the semaphore will be awaited synchronously for a very short time even in the asynchronous methods to try to avoid this allocation.
This library still guarantees that no two threads will ever hold a lock with the same key.
However, depending on the amount of unique keys at runtime and the numberOfSemaphores
parameter passed to the KeyedSemaphoresCollection
,
it is now possible that two threads with different keys share the same underlying semaphore and have to wait for each other.
- Handle exotic edge case where two threads could hold the same keyed semaphore simultaneously
- Add support to check if key already locked with IsInUse API (#45)
- Add support for timeout with TryLock(Async) API
- Fix issue where keyed semaphores were not released when the cancellationToken triggers
- Remove interfaces, just use KeyedSemaphore and KeyedSemaphoreCollection
- Remove a lot of API surface, just use KeyedSemaphore.Lock(Async) or KeyedSemaphoreCollection.Lock(Async)
- KeyedSemaphoreCollection and KeyedSemaphore are no longer IDisposable
- Drastically improve performance and reduce allocations
- Remove
IEquatable
constraint for keys
- Add benchmarks (#14)
- Add support for generic keys (#15)
- Add support for multiple keyed semaphore collections (#16)
Add support for synchronous locking, a contribution by @roel-de-regt. See #1 for more info
Properly dispose of inner SemaphoreSlim instances. This is only necessary if you actually use the AvailableWaitHandle on SemaphoreSlim - which this library doesn't - but it's better to be a good citizen and call Dispose at the appropriate times.
To make sure disposal triggers immediate cancellation of any pending wait operations, the inner SemaphoreSlim instance is no longer exposed as a property. Instead, the "WaitAsync" and "Release" methods are exposed on IKeyedSemaphore directly.
This is only relevant if you are already using KeyedSemaphore.GetOrCreate and using the IKeyedSemaphore directly. If you only use KeyedSemaphore.LockAsync, this change does not impact you.
Because it is a breaking change, I'm marking it as 2.0.0, but fixing the breaking change in your code should be very straightforward. Simply replace all instances
of keyedSemaphore.Semaphore.WaitAsync
with keyedSemaphore.WaitAsync
. The same goes for Release
.
If you were using properties of SemaphoreSlim other than the ones that I've surfaced on IKeyedSemaphore, raise an issue here and we'll talk about it!
Add convenience method to acquire AND lock keyed semaphore in one go
Fix namespace
Initial release