public void AddRevertAction_ShouldBeCopiedWhenCompleteed() { Stack <int> stack = new Stack <int>(); stack.Push(1); stack.Push(2); stack.Push(11); stack.Push(12); stack.Push(3); using (var ct = new AtomicComposition()) { ct.AddRevertAction(() => Assert.Equal(1, stack.Pop())); ct.AddRevertAction(() => Assert.Equal(2, stack.Pop())); using (var ct2 = new AtomicComposition(ct)) { ct2.AddRevertAction(() => Assert.Equal(11, stack.Pop())); ct2.AddRevertAction(() => Assert.Equal(12, stack.Pop())); // completeting should move those revert actions to ct ct2.Complete(); Assert.Equal(5, stack.Count); } ct.AddRevertAction(() => Assert.Equal(3, stack.Pop())); // Do not complete let ct dispose and revert } Assert.True(stack.Count == 0); }
private void CopyComplete() { Assumes.NotNull(_outerAtomicComposition); _outerAtomicComposition.ContainsInnerAtomicComposition = false; // Inner scopes are much odder, because completeting them means coalescing them into the // outer scope - the complete or revert actions are deferred until the outermost scope completes // or any intermediate rolls back if (_completeActionList != null) { foreach (Action action in _completeActionList) { _outerAtomicComposition.AddCompleteAction(action); } } if (_revertActionList != null) { foreach (Action action in _revertActionList) { _outerAtomicComposition.AddRevertAction(action); } } // We can copy over existing atomicComposition entries because they're either already chained or // overwrite by design and can now be completed or rolled back together for (var index = 0; index < _valueCount; index++) { _outerAtomicComposition.SetValueInternal( _values[index].Key, _values[index].Value); } }
public void AddRevertAction_ShouldExecuteInReverseOrder() { var ct = new AtomicComposition(); Stack <int> stack = new Stack <int>(); stack.Push(1); stack.Push(2); stack.Push(3); ct.AddRevertAction(() => Assert.Equal(1, stack.Pop())); ct.AddRevertAction(() => Assert.Equal(2, stack.Pop())); ct.AddRevertAction(() => Assert.Equal(3, stack.Pop())); ct.Dispose(); Assert.True(stack.Count == 0); }
public void AddRevertAction_ShouldExecuteWhenDisposedAndNotCompleteted() { var ct = new AtomicComposition(); bool executed = false; ct.AddRevertAction(() => executed = true); ct.Dispose(); Assert.True(executed); }
/// <summary> /// Previews all the required imports for the given <see cref="ComposablePart"/> to /// ensure they can all be satisified. The preview does not actually set the imports /// only ensures that they exist in the source provider. If the preview succeeds then /// the <see cref="ImportEngine"/> also enforces that changes to exports in the source /// provider will not break any of the required imports. If this enforcement needs to be /// lifted for this part then <see cref="ReleaseImports"/> needs to be called for this /// <see cref="ComposablePart"/>. /// </summary> /// <param name="part"> /// The <see cref="ComposablePart"/> to preview the required imports. /// </param> /// <param name="atomicComposition"></param> /// <exception cref="CompositionException"> /// An error occurred during previewing and <paramref name="atomicComposition"/> is null. /// <see cref="CompositionException.Errors"/> will contain a collection of errors that occurred. /// The pre-existing composition is in an unknown state, depending on the errors that occured. /// </exception> /// <exception cref="ChangeRejectedException"> /// An error occurred during the previewing and <paramref name="atomicComposition"/> is not null. /// <see cref="CompositionException.Errors"/> will contain a collection of errors that occurred. /// The pre-existing composition remains in valid state. /// </exception> /// <exception cref="ObjectDisposedException"> /// The <see cref="ImportEngine"/> has been disposed of. /// </exception> public void PreviewImports(ComposablePart part, AtomicComposition atomicComposition) { this.ThrowIfDisposed(); Requires.NotNull(part, "part"); // Do not do any previewing if SilentRejection is disabled. if (this._compositionOptions.HasFlag(CompositionOptions.DisableSilentRejection)) { return; } // NOTE : this is a very intricate area threading-wise, please use caution when changing, otherwise state corruption or deadlocks will ensue // The gist of what we are doing is as follows: // We need to lock the composition, as we will proceed modifying our internal state. The tricky part is when we release the lock // Due to the fact that some actions will take place AFTER we leave this method, we need to KEEP THAT LOCK HELD until the transation is commiited or rolled back // This is the reason we CAN'T use "using here. // Instead, if the transaction is present we will queue up the release of the lock, otherwise we will release it when we exit this method // We add the "release" lock to BOTH Commit and Revert queues, because they are mutually exclusive, and we need to release the lock regardless. // This will take the lock, if necesary IDisposable compositionLockHolder = this._lock.IsThreadSafe ? this._lock.LockComposition() : null; bool compositionLockTaken = (compositionLockHolder != null); try { // revert actions are processed in the reverse order, so we have to add the "release lock" action now if (compositionLockTaken && (atomicComposition != null)) { atomicComposition.AddRevertAction(() => compositionLockHolder.Dispose()); } var partManager = GetPartManager(part, true); var result = TryPreviewImportsStateMachine(partManager, part, atomicComposition); result.ThrowOnErrors(atomicComposition); StartSatisfyingImports(partManager, atomicComposition); // Add the "release lock" to the commit actions if (compositionLockTaken && (atomicComposition != null)) { atomicComposition.AddCompleteAction(() => compositionLockHolder.Dispose()); } } finally { // We haven't updated the queues, so we can release the lock now if (compositionLockTaken && (atomicComposition == null)) { compositionLockHolder.Dispose(); } } }
internal static void AddRevertActionAllowNull(this AtomicComposition atomicComposition, Action action) { Assumes.NotNull(action); if (atomicComposition == null) { action(); } else { atomicComposition.AddRevertAction(action); } }
public void SetSavedImport(ImportDefinition import, Export[] exports, AtomicComposition atomicComposition) { if (atomicComposition != null) { var savedExports = GetSavedImport(import); // Add a revert action to revert the stored exports // in the case that this atomicComposition gets rolled back. atomicComposition.AddRevertAction(() => SetSavedImport(import, savedExports, null)); } if (_importCache == null) { _importCache = new Dictionary <ImportDefinition, Export[]>(); } _importCache[import] = exports; }