// Must be called with HandlesLock held. // This is called when a reference becomes the oldest in-progress reference for an instance. This triggers the end of resolution for markers. // Returns false if the resolution failed, meaning that the marker can be removed. bool CheckOldestReference(InstanceHandleReference handleRef, ref List <InstanceHandleReference> handlesPendingResolution) { LockResolutionMarker marker = handleRef as LockResolutionMarker; if (marker == null || marker.IsComplete) { return(true); } bool returnValue = true; try { InstanceHandle existingHandle; if (BoundHandles.TryGetValue(marker.InstanceHandle.Id, out existingHandle)) { Fx.AssertAndFailFast(!object.ReferenceEquals(existingHandle, marker.InstanceHandle), "InstanceStore lock state is not correct in CheckOldestReference."); if (existingHandle.Version <= 0 || marker.InstanceVersion <= 0) { if (existingHandle.Version != 0 || marker.InstanceVersion != 0) { marker.Reason = new InvalidOperationException(SRCore.InvalidLockToken); returnValue = false; } else { marker.ConflictingHandle = existingHandle; returnValue = false; } } else if (existingHandle.Version >= marker.InstanceVersion) { marker.ConflictingHandle = existingHandle; returnValue = false; } } // No other handles have committed a bind to this or a higher version! We are ok to do so, but it is still not committed, so we stay in queue. return(returnValue); } finally { marker.NotifyMarkerComplete(returnValue); if (handlesPendingResolution == null) { handlesPendingResolution = new List <InstanceHandleReference>(1); } handlesPendingResolution.Add(marker); } }
// This can be called to remove a handle from the BoundHandles table. It should be called only after no more commands are in progress or could be made on the handle. internal void Unbind(InstanceHandle handle) { Fx.Assert(object.ReferenceEquals(this, handle.Owner), "Unbind called on the wrong owner for a handle."); Fx.Assert(handle.Id != Guid.Empty, "Unbind called on a handle not even bound to an instance."); lock (HandlesLock) { // The handle may have already been bumped - only remove it if it's still it. if (BoundHandles.TryGetValue(handle.Id, out InstanceHandle existingHandle) && object.ReferenceEquals(handle, existingHandle)) { BoundHandles.Remove(handle.Id); } } }
// This is called if we found an existing lock. This handle doesn't own the lock, but it could claim it, if it can prove // that no other live handle owns it. If this returns non-null, the outcome will be available later on the // InstanceHandleReference once the AsyncWaitHandle completes. (Null indicates a conflict with another handle.) // // The instanceVersion reported here was read under the transaction, but not changed. Either it was already committed, or it was written under // this transaction in a prior command on a different handle. Due to the latter case, we treat it as dirty - we do not publish it or take // any visible action (such as dooming handles) based on its value. internal AsyncWaitHandle InitiateLockResolution(long instanceVersion, ref InstanceHandleReference reference, ref List <InstanceHandleReference> handlesPendingResolution) { Fx.Assert(reference != null, "Bind wasn't registered - RegisterStartBind must be called."); Fx.Assert(reference.InstanceHandle != null, "Cannot cancel and complete a bind."); Fx.Assert(reference.InstanceHandle.Id != Guid.Empty, "Must be bound to an instance already."); Fx.AssertAndThrow(!(reference is LockResolutionMarker), "InitiateLockResolution already called."); lock (HandlesLock) { InstanceHandleReference cancelReference = reference; LockResolutionMarker markerReference = null; try { InstanceHandle existingHandle; if (BoundHandles.TryGetValue(reference.InstanceHandle.Id, out existingHandle)) { Fx.AssertAndFailFast(!object.ReferenceEquals(existingHandle, reference.InstanceHandle), "InstanceStore lock state is not correct in InitiateLockResolution."); if (existingHandle.Version <= 0 || instanceVersion <= 0) { if (existingHandle.Version != 0 || instanceVersion != 0) { throw Fx.Exception.AsError(new InvalidOperationException(SRCore.InvalidLockToken)); } reference.InstanceHandle.ConflictingHandle = existingHandle; return(null); } if (existingHandle.Version >= instanceVersion) { reference.InstanceHandle.ConflictingHandle = existingHandle; return(null); } } // Put a marker in the InProgressHandles. If it makes it through, and there's still no conflicting handle, // then the lock can be claimed at this version. Only currently in-progress bindings have a chance of // staking a stronger claim to the lock version (if the store actually acquired the lock for the handle). markerReference = new LockResolutionMarker(reference.InstanceHandle, instanceVersion); EnqueueReference(markerReference); reference = markerReference; Fx.Assert(markerReference.MarkerWaitHandle != null, "Null MarkerWaitHandle?"); return(markerReference.MarkerWaitHandle); } finally { if (!object.ReferenceEquals(markerReference, reference)) { CancelReference(ref reference, ref handlesPendingResolution); if (markerReference != null) { cancelReference = markerReference; CancelReference(ref cancelReference, ref handlesPendingResolution); } } else { CancelReference(ref cancelReference, ref handlesPendingResolution); } } } }
// This happens only when the transaction under which the handle was bound is committed. internal bool TryCompleteBind(ref InstanceHandleReference reference, ref List <InstanceHandleReference> handlesPendingResolution, out InstanceHandle handleToFree) { Fx.Assert(reference != null, "Bind wasn't registered - RegisterStartBind must be called."); Fx.Assert(reference.InstanceHandle != null, "Cannot cancel and complete a bind."); Fx.Assert(reference.InstanceHandle.Version != -1, "Handle state must be set first."); Fx.Assert(object.ReferenceEquals(this, reference.InstanceHandle.Owner), "TryCompleteBind called on the wrong owner for a handle."); Fx.Assert(!(reference is LockResolutionMarker) || ((LockResolutionMarker)reference).NonConflicting, "How did a Version get set if we're still resolving."); handleToFree = null; lock (HandlesLock) { try { InstanceHandle existingHandle; if (BoundHandles.TryGetValue(reference.InstanceHandle.Id, out existingHandle)) { Fx.AssertAndFailFast(!object.ReferenceEquals(existingHandle, reference.InstanceHandle), "InstanceStore lock state is not correct."); if (existingHandle.Version <= 0 || reference.InstanceHandle.Version <= 0) { if (existingHandle.Version != 0 || reference.InstanceHandle.Version != 0) { throw Fx.Exception.AsError(new InvalidOperationException(SRCore.InvalidLockToken)); } reference.InstanceHandle.ConflictingHandle = existingHandle; return(false); } if (existingHandle.Version > reference.InstanceHandle.Version) { reference.InstanceHandle.ConflictingHandle = existingHandle; return(false); } if (existingHandle.Version < reference.InstanceHandle.Version) { existingHandle.ConflictingHandle = reference.InstanceHandle; handleToFree = existingHandle; BoundHandles[reference.InstanceHandle.Id] = reference.InstanceHandle; return(true); } if (existingHandle.Version == reference.InstanceHandle.Version) { // This could be a case of amnesia (backup / restore). throw Fx.Exception.AsError(new InvalidOperationException(SRCore.InstanceStoreBoundSameVersionTwice)); } throw Fx.AssertAndThrow("All cases covered above."); } else { BoundHandles.Add(reference.InstanceHandle.Id, reference.InstanceHandle); return(true); } } finally { CancelReference(ref reference, ref handlesPendingResolution); } } }