diff options
Diffstat (limited to 'lib/Utils')
-rw-r--r-- | lib/Utils/src/Async/AsyncAccessSerializer.cs | 88 | ||||
-rw-r--r-- | lib/Utils/src/VNLib.Utils.csproj | 12 |
2 files changed, 72 insertions, 28 deletions
diff --git a/lib/Utils/src/Async/AsyncAccessSerializer.cs b/lib/Utils/src/Async/AsyncAccessSerializer.cs index bdd8114..fa164ac 100644 --- a/lib/Utils/src/Async/AsyncAccessSerializer.cs +++ b/lib/Utils/src/Async/AsyncAccessSerializer.cs @@ -177,38 +177,47 @@ namespace VNLib.Utils.Async * * When there are no more waiters for a moniker at the time the lock was entered, the WaitEntry is released * back to the pool. - */ + * + * Since tasks are cancellable by another thread at any time, it is possible that the canceled task will be + * dequeued as the next task to transition. This condition is guarded by the release token by returning a boolean + * signalling the transition failure, if so, must repeat the release process, until a valid release occurs + * or the final release is issued. + */ WaitReleaseToken releaser; - lock (StoreLock) + do { - WaitEntry entry = WaitTable[moniker]; - - //Call release while holding store lock - if (entry.ExitWait(out releaser) == 0) + lock (StoreLock) { - //No more waiters - WaitTable.Remove(moniker); + WaitEntry entry = WaitTable[moniker]; + + //Call release while holding store lock + if (entry.ExitWait(out releaser) == 0) + { + //No more waiters + WaitTable.Remove(moniker); - /* - * We must release the semaphore before returning to pool, - * its safe because there are no more waiters - */ + /* + * We must release the semaphore before returning to pool, + * its safe because there are no more waiters + */ - Debug.Assert(!releaser.WillTransition, "The wait entry referrence count was 0 but a release token was issued that would cause a lock transision"); + Debug.Assert(!releaser.WillTransition, "The wait entry referrence count was 0 but a release token was issued that would cause a lock transision"); - releaser.Release(); + releaser.Release(); - ReturnEntry(entry); + ReturnEntry(entry); - //already released - releaser = default; + return; + } } - } - //Release sem outside of lock - releaser.Release(); + /* + * If the releaser fails to transition the next task, we need to repeat the + * release process to ensure that at least one waiter is properly released + */ + } while (!releaser.Release()); } @@ -483,9 +492,44 @@ namespace VNLib.Utils.Async /// <summary> /// Releases the exclusive lock held by the token. NOTE: /// this method may only be called ONCE after a wait has been - /// released + /// released. + /// <para> + /// If <see cref="WillTransition"/> is true, this method may cause a waiting + /// task to transition. The result must be examined to determine if the + /// transition was successful.If a transition is not successful, then a deadlock may occur if + /// another waiter is not selected. + /// </para> /// </summary> - public readonly void Release() => _nextWaiter?.Start(); + /// <returns>A value that indicates if the task was transition successfully</returns> + public readonly bool Release() + { + //return success if no next waiter + if(_nextWaiter == null) + { + return true; + } + + /* + * Guard against the next waiter being cancelled, + * this thread could be suspended after this check + * but for now should be good enough. An exception + * will be thrown if this doesnt work + */ + + if (_nextWaiter.Status == TaskStatus.Created) + { + _nextWaiter.Start(); + return true; + } + + if(_nextWaiter.Status == TaskStatus.Canceled) + { + return false; + } + + Debug.Fail($"Next waiting task is in an invalid state: {_nextWaiter.Status}"); + return false; + } } /// <summary> diff --git a/lib/Utils/src/VNLib.Utils.csproj b/lib/Utils/src/VNLib.Utils.csproj index 404beaa..d121183 100644 --- a/lib/Utils/src/VNLib.Utils.csproj +++ b/lib/Utils/src/VNLib.Utils.csproj @@ -17,13 +17,12 @@ <Company>Vaughn Nugent</Company> <Product>VNLib Utilities Library</Product> <Copyright>Copyright © 2023 Vaughn Nugent</Copyright> - <Description> - .NET/6.0 Utilities library for high-performance common operations. Utilities and abstractions for building and - diagnosing native memory implementations. Dyanmic native library loading, IO, extensions, data encoding, resource - access, and asynchronous cooperation primitives. - </Description> + <Description>.NET/6.0 Utilities library for high-performance common operations. Utilities and abstractions for building and diagnosing native memory implementations. Dyanmic native library loading, IO, extensions, data encoding, resource access, and asynchronous cooperation primitives.</Description> <PackageProjectUrl>https://www.vaughnnugent.com/resources/software/modules/VNLib.Core</PackageProjectUrl> <RepositoryUrl>https://github.com/VnUgE/VNLib.Core/tree/main/lib/Utils</RepositoryUrl> + <PackageReadmeFile>README.md</PackageReadmeFile> + <PackageLicenseFile>LICENSE</PackageLicenseFile> + <PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance> </PropertyGroup> <ItemGroup> @@ -34,9 +33,10 @@ <None Include="..\LICENSE"> <Pack>True</Pack> <PackagePath>\</PackagePath> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> </None> </ItemGroup> - + <PropertyGroup Condition="'$(Configuration)'=='Debug'"> <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow> </PropertyGroup> |