aboutsummaryrefslogtreecommitdiff
path: root/lib/Utils
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Utils')
-rw-r--r--lib/Utils/src/Async/AsyncAccessSerializer.cs88
-rw-r--r--lib/Utils/src/VNLib.Utils.csproj12
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>