| | | 1 | | //======================================================================= |
| | | 2 | | // NavigationOperationReceipt.cs |
| | | 3 | | //======================================================================= |
| | | 4 | | // MIT License, Copyright (c) 2024-present David Oravsky (mrdav30) |
| | | 5 | | // See LICENSE file in the project root for full license information. |
| | | 6 | | //======================================================================= |
| | | 7 | | |
| | | 8 | | using System.Threading; |
| | | 9 | | |
| | | 10 | | namespace Trailblazer.Pathing; |
| | | 11 | | |
| | | 12 | | /// <summary>Identifies the deterministic lifecycle state of a submitted navigation operation.</summary> |
| | | 13 | | public enum NavigationOperationStatus |
| | | 14 | | { |
| | | 15 | | /// <summary>The operation is admitted but has not published or terminated.</summary> |
| | | 16 | | Pending = 0, |
| | | 17 | | |
| | | 18 | | /// <summary>The operation was included in an immutable candidate publication.</summary> |
| | | 19 | | Applied = 1, |
| | | 20 | | |
| | | 21 | | /// <summary>The operation failed validation or admission without changing the candidate.</summary> |
| | | 22 | | Rejected = 2, |
| | | 23 | | |
| | | 24 | | /// <summary>A later operation made the complete operation observably redundant.</summary> |
| | | 25 | | Superseded = 3 |
| | | 26 | | } |
| | | 27 | | |
| | | 28 | | /// <summary>Identifies why a navigation operation was rejected.</summary> |
| | | 29 | | public enum NavigationOperationRejection |
| | | 30 | | { |
| | | 31 | | /// <summary>The receipt is not rejected.</summary> |
| | | 32 | | None = 0, |
| | | 33 | | |
| | | 34 | | /// <summary>The operation descriptor failed structural validation.</summary> |
| | | 35 | | InvalidOperation = 1, |
| | | 36 | | |
| | | 37 | | /// <summary>The supplied operation sequence was already admitted.</summary> |
| | | 38 | | DuplicateSequence = 2, |
| | | 39 | | |
| | | 40 | | /// <summary>The supplied operation sequence was below the admitted high-water mark.</summary> |
| | | 41 | | RegressingSequence = 3, |
| | | 42 | | |
| | | 43 | | /// <summary>The effective frame regressed below the admitted frame high-water mark.</summary> |
| | | 44 | | RegressingEffectiveFrame = 4, |
| | | 45 | | |
| | | 46 | | /// <summary>The effective-frame boundary had already begun when the operation was submitted.</summary> |
| | | 47 | | LateEffectiveFrame = 5, |
| | | 48 | | |
| | | 49 | | /// <summary>A finite count or byte capacity would be exceeded.</summary> |
| | | 50 | | CapacityExceeded = 6, |
| | | 51 | | |
| | | 52 | | /// <summary>The operation referenced a map that was not present in the candidate.</summary> |
| | | 53 | | MissingMap = 7, |
| | | 54 | | |
| | | 55 | | /// <summary>The operation was based on a stale checkpoint stamp.</summary> |
| | | 56 | | Stale = 8, |
| | | 57 | | |
| | | 58 | | /// <summary>The complete candidate failed validation.</summary> |
| | | 59 | | ValidationFailed = 9 |
| | | 60 | | } |
| | | 61 | | |
| | | 62 | | /// <summary> |
| | | 63 | | /// Exposes one thread-safe terminal result for a deterministic navigation operation. |
| | | 64 | | /// </summary> |
| | | 65 | | public sealed class NavigationOperationReceipt |
| | | 66 | | { |
| | | 67 | | private int _status; |
| | | 68 | | private int _rejection; |
| | 1815 | 69 | | private int _publishedFrame = -1; |
| | | 70 | | private int _admissionClaimed; |
| | | 71 | | private int _completionClaimed; |
| | | 72 | | |
| | 1815 | 73 | | internal NavigationOperationReceipt(long operationSequence, int effectiveFrame) |
| | | 74 | | { |
| | 1815 | 75 | | OperationSequence = operationSequence; |
| | 1815 | 76 | | EffectiveFrame = effectiveFrame; |
| | 1815 | 77 | | } |
| | | 78 | | |
| | | 79 | | /// <summary>Gets the host-supplied operation sequence.</summary> |
| | | 80 | | public long OperationSequence { get; } |
| | | 81 | | |
| | | 82 | | /// <summary>Gets the earliest fixed-step frame at which this operation may become visible.</summary> |
| | | 83 | | public int EffectiveFrame { get; } |
| | | 84 | | |
| | | 85 | | /// <summary>Gets the current lifecycle state.</summary> |
| | 20562 | 86 | | public NavigationOperationStatus Status => (NavigationOperationStatus)Volatile.Read(ref _status); |
| | | 87 | | |
| | | 88 | | /// <summary>Gets the rejection reason when <see cref="Status"/> is Rejected.</summary> |
| | | 89 | | public NavigationOperationRejection Rejection => |
| | 353 | 90 | | (NavigationOperationRejection)Volatile.Read(ref _rejection); |
| | | 91 | | |
| | | 92 | | /// <summary>Gets the actual publication frame, or -1 before publication.</summary> |
| | 16 | 93 | | public int PublishedFrame => Volatile.Read(ref _publishedFrame); |
| | | 94 | | |
| | | 95 | | internal bool TryClaimAdmission() => |
| | 1818 | 96 | | Interlocked.CompareExchange(ref _admissionClaimed, 1, 0) == 0; |
| | | 97 | | |
| | | 98 | | internal void CompleteApplied(int publishedFrame) |
| | | 99 | | { |
| | 1503 | 100 | | Complete(NavigationOperationStatus.Applied, NavigationOperationRejection.None, publishedFrame); |
| | 1503 | 101 | | } |
| | | 102 | | |
| | | 103 | | internal void CompleteRejected(NavigationOperationRejection rejection) |
| | | 104 | | { |
| | 281 | 105 | | SwiftThrowHelper.ThrowIfArgument( |
| | 281 | 106 | | rejection == NavigationOperationRejection.None, |
| | 281 | 107 | | nameof(rejection), |
| | 281 | 108 | | "A rejected receipt requires a reason."); |
| | 281 | 109 | | Complete(NavigationOperationStatus.Rejected, rejection, publishedFrame: -1); |
| | 281 | 110 | | } |
| | | 111 | | |
| | | 112 | | internal void CompleteSuperseded() => |
| | 17 | 113 | | Complete(NavigationOperationStatus.Superseded, NavigationOperationRejection.None, publishedFrame: -1); |
| | | 114 | | |
| | | 115 | | private void Complete( |
| | | 116 | | NavigationOperationStatus status, |
| | | 117 | | NavigationOperationRejection rejection, |
| | | 118 | | int publishedFrame) |
| | | 119 | | { |
| | 1801 | 120 | | SwiftThrowHelper.ThrowIfTrue( |
| | 1801 | 121 | | Interlocked.CompareExchange(ref _completionClaimed, 1, 0) != 0, |
| | 1801 | 122 | | nameof(NavigationOperationReceipt), |
| | 1801 | 123 | | "A navigation operation receipt may complete only once."); |
| | | 124 | | |
| | 1801 | 125 | | Volatile.Write(ref _rejection, (int)rejection); |
| | 1801 | 126 | | Volatile.Write(ref _publishedFrame, publishedFrame); |
| | 1801 | 127 | | Volatile.Write(ref _status, (int)status); |
| | 1801 | 128 | | } |
| | | 129 | | } |