IVSoftware.Portable.Disposable.AuthorityEpochProvider
1.0.3
Prefix Reserved
dotnet add package IVSoftware.Portable.Disposable.AuthorityEpochProvider --version 1.0.3
NuGet\Install-Package IVSoftware.Portable.Disposable.AuthorityEpochProvider -Version 1.0.3
<PackageReference Include="IVSoftware.Portable.Disposable.AuthorityEpochProvider" Version="1.0.3" />
<PackageVersion Include="IVSoftware.Portable.Disposable.AuthorityEpochProvider" Version="1.0.3" />
<PackageReference Include="IVSoftware.Portable.Disposable.AuthorityEpochProvider" />
paket add IVSoftware.Portable.Disposable.AuthorityEpochProvider --version 1.0.3
#r "nuget: IVSoftware.Portable.Disposable.AuthorityEpochProvider, 1.0.3"
#:package IVSoftware.Portable.Disposable.AuthorityEpochProvider@1.0.3
#addin nuget:?package=IVSoftware.Portable.Disposable.AuthorityEpochProvider&version=1.0.3
#tool nuget:?package=IVSoftware.Portable.Disposable.AuthorityEpochProvider&version=1.0.3
AuthorityEpochProvider [Github]
Coordinates a shared, reference-counted authority across overlapping scopes.
Problem
Multiple participants need to operate within a shared context, but:
- The first participant defines the controlling authority
- Additional participants may join without overriding that authority
- Cleanup must occur once, when the last participant exits
- Reentry and cancellation must not corrupt state or leak events
Solution
AuthorityEpochProvider wraps a reference-counted lifetime (DisposableHost) and projects:
- Stable authority (first requester wins)
- Scoped participation via
IDisposable - Deterministic teardown on the 1 → 0 transition
- Safe cancellation that detaches the current epoch without poisoning the instance
Usage
var aep = new AuthorityEpochProvider();
using (aep.RequestAuthority(MyAuthority.A1))
{
// A1 is the authority for this epoch
using (aep.RequestAuthority(MyAuthority.A2))
{
// A2 participates, but authority remains A1
}
}
// FinalDispose raised once here
Key Behaviors
- Authority is established once (0 → 1 transition)
- Authority remains stable until all participants exit
- Nested and overlapping requests do not override authority
- FinalDispose fires once when the last token is released
- IsDisposing is true during FinalDispose
- CancelAuthorityEpoch:
- Ends the current epoch immediately
- Suppresses FinalDispose
- Allows reuse on the next line
An optional shared ephemeral context (see Context below) is available while tokens are active.
Token Ring Mental Model
Authorities can be treated as positions in a logical sequence (e.g., A1 -> A2 -> A3).
- Any participant may enter the epoch at any position
- The first entrant defines the starting point
- Subsequent participants may join at any position without changing the start
- Downstream logic can interpret authority relative to that starting point
Example:
using (aep.RequestAuthority(A2))
{
// Epoch starts at A2
using (aep.RequestAuthority(A3))
using (aep.RequestAuthority(A1))
{
// Participants span the ring, but authority remains A2
}
}
This enables scenarios where work proceeds in a predictable cycle, anchored to the first participant’s entry point.
Notes
HasRequestedAuthorityreflects active participation, not history- Cancellation detaches the provider from the current epoch; existing scopes complete silently
- Thread-safe via underlying host
Context (Optional)
AuthorityEpochProvider inherits dictionary semantics from DisposableHost:
- Participants may contribute key/value pairs during the epoch
- Values are aggregated across participants
- A final, immutable snapshot is emitted on
FinalDispose
While not required for authority coordination, this enables a powerful pattern: a shared, mutable context that evolves across the epoch and resolves deterministically at teardown.
While AuthorityEpochProvider is intended for production code, there is also utility when it comes to unit testing. Version 1.0.3 adds this handy test utility.
TestableEpoch
Deterministic Test Data
TestableEpoch is useful when a unit test needs fast, repeatable
fixture data.
The pattern is simple:
- Write production-style code such as
Guid.NewGuid().WithTestability().ToString() - Wrap the fixture builder in
using var te = this.TestableEpoch(); - Capture the generated text model once
- Paste that text back into
expected
Why this helps:
- The call site stays natural
- The generated ids and timestamps become deterministic inside the test
- Large copied expectations remain stable across runs
- Test writing stays fast because "generate, inspect, paste" is safe
Why this matters:
If a production model like PlaceableModel is allowed to create real
Guids during the test, then the text model changes every run. The copied
expected value becomes noise instead of a useful assertion.
With TestableEpoch, the same fixture builder produces the same ids and
timestamps every time, so verbose modeled output can be captured and
asserted directly.
Typical use:
[TestMethod, DoNotParallelize]
public void Test_Something()
{
using var te = this.TestableEpoch();
var id = Guid.NewGuid().WithTestability().ToString();
}
DoNotParallelize is important because TestableEpoch uses static state
and must remain isolated per test.
This is especially handy for tests that build on-the-fly XML or text models, inspect the result once, and then lock it in as the expected value.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- IVSoftware.Portable.Common (>= 1.0.1)
- IVSoftware.Portable.Disposable (>= 2.0.0)
- IVSoftware.Portable.Xml.Linq.XBoundObject (>= 2.0.3)
- Newtonsoft.Json (>= 13.0.3)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version 1.0.0 - Initial release.
* Introduces IAuthorityEpochProvider for coordinated authority-based epochs.
* Establishes first-wins authority model with ordered participation tracking.
* Integrates with DisposableHost for deterministic FinalDispose semantics.
* Supports cancellation of FinalDispose event without interrupting epoch teardown.
Version 1.0.1
* Adds runtime type enforcement to generic version via Throw pattern.
* Expands test coverage for cancellation, mixed authority scenarios, and lifecycle invariants.
* Validates dictionary-backed context aggregation and FinalDispose snapshot semantics.
* Confirms awaitable behavior aligned with epoch completion.
Version 1.0.2
* Adds a default value for the CancelAuthorityEpoch @throw parameter (@throw = false).
* Promotes generic Authority and Authorities to public members for better discoverability.
* Reduces reliance on explicit interface access for common scenarios.
* No breaking changes.
Version 1.0.3
* Adds TestableEpoch support for deterministic Guid and DateTimeOffset generation in unit tests.
* Introduces WithTestability extension methods that preserve natural production call sites.
* Documents the TestableEpoch workflow in the package README.
* Supports expanded MSTest coverage including deterministic test fixture generation.