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);
        }
Ejemplo n.º 2
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);
        }
Ejemplo n.º 5
0
        /// <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;
            }