コード例 #1
0
ファイル: ImportEngine.cs プロジェクト: zqb971/mef
        /// <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();
                }
            }
        }
コード例 #2
0
        public void Complete_ShouldExecuteActions()
        {
            bool executedAction = false;

            var ct = new AtomicComposition();

            ct.AddCompleteAction(() => executedAction = true);

            ct.Complete();

            Assert.True(executedAction);
        }
コード例 #3
0
        internal static void AddCompleteActionAllowNull(this AtomicComposition atomicComposition, Action action)
        {
            Assumes.NotNull(action);

            if (atomicComposition == null)
            {
                action();
            }
            else
            {
                atomicComposition.AddCompleteAction(action);
            }
        }
コード例 #4
0
        public void AfterComplete_AllMethodsShouldThrow()
        {
            var ct = new AtomicComposition();

            ct.Complete();

            Assert.Throws <InvalidOperationException>(() => ct.AddCompleteAction(() => ct = null));
            Assert.Throws <InvalidOperationException>(() => ct.Complete());
            Assert.Throws <InvalidOperationException>(() => ct.SetValue(ct, 10));
            object value;

            Assert.Throws <InvalidOperationException>(() => ct.TryGetValue(ct, out value));
        }
コード例 #5
0
        public void Dispose_AllMethodsShouldThrow()
        {
            var ct = new AtomicComposition();

            ct.Dispose();

            Assert.Throws <ObjectDisposedException>(() => ct.AddCompleteAction(() => ct = null));
            Assert.Throws <ObjectDisposedException>(() => ct.Complete());
            Assert.Throws <ObjectDisposedException>(() => ct.SetValue(ct, 10));
            object value;

            Assert.Throws <ObjectDisposedException>(() => ct.TryGetValue(ct, out value));
        }
コード例 #6
0
ファイル: ImportEngine.cs プロジェクト: mikem8361/runtime
        private EngineContext GetEngineContext(AtomicComposition atomicComposition)
        {
            ArgumentNullException.ThrowIfNull(atomicComposition);

            if (!atomicComposition.TryGetValue(this, true, out EngineContext? engineContext))
            {
                atomicComposition.TryGetValue(this, false, out EngineContext? parentContext);
                engineContext = new EngineContext(this, parentContext);
                atomicComposition.SetValue(this, engineContext);
                atomicComposition.AddCompleteAction(engineContext.Complete);
            }
            return(engineContext !);
        }
コード例 #7
0
ファイル: CatalogExportProvider.cs プロジェクト: keutmann/SPM
        private void OnCatalogChanging(object sender, ComposablePartCatalogChangeEventArgs e)
        {
            using (var atomicComposition = new AtomicComposition(e.AtomicComposition))
            {
                // Save the preview catalog to use in place of the original while handling
                // this event
                atomicComposition.SetValue(this._catalog,
                                           new CatalogChangeProxy(this._catalog, e.AddedDefinitions, e.RemovedDefinitions));

                IEnumerable <ExportDefinition> addedExports = e.AddedDefinitions
                                                              .SelectMany(part => part.ExportDefinitions)
                                                              .ToArray();
                IEnumerable <ExportDefinition> removedExports = e.RemovedDefinitions
                                                                .SelectMany(part => part.ExportDefinitions)
                                                                .ToArray();

                // Remove any parts based on eliminated definitions (in a atomicComposition-friendly
                // fashion)
                foreach (var definition in e.RemovedDefinitions)
                {
                    ComposablePart removedPart = null;
                    bool           removed     = false;

                    using (new ReadLock(_lock))
                    {
                        removed = this._activatedParts.TryGetValue(definition, out removedPart);
                    }
                    if (removed)
                    {
                        ReleasePart(null, removedPart, atomicComposition);
                        atomicComposition.AddCompleteActionAllowNull(() =>
                        {
                            using (new WriteLock(_lock))
                            {
                                this._activatedParts.Remove(definition);
                            }
                        });
                    }
                }

                UpdateRejections(addedExports.ConcatAllowingNull(removedExports), atomicComposition);

                this.OnExportsChanging(
                    new ExportsChangeEventArgs(addedExports, removedExports, atomicComposition));

                atomicComposition.AddCompleteAction(() => this.OnExportsChanged(
                                                        new ExportsChangeEventArgs(addedExports, removedExports, null)));

                atomicComposition.Complete();
            }
        }
コード例 #8
0
        private void OnCatalogChanging(object sender, ComposablePartCatalogChangeEventArgs e)
        {
            using (var atomicComposition = new AtomicComposition(e.AtomicComposition))
            {
                // Save the preview catalog to use in place of the original while handling
                // this event
                atomicComposition.SetValue(_catalog,
                                           new CatalogChangeProxy(_catalog, e.AddedDefinitions, e.RemovedDefinitions));

                IEnumerable <ExportDefinition> addedExports   = GetExportsFromPartDefinitions(e.AddedDefinitions);
                IEnumerable <ExportDefinition> removedExports = GetExportsFromPartDefinitions(e.RemovedDefinitions);

                // Remove any parts based on eliminated definitions (in a atomicComposition-friendly
                // fashion)
                foreach (var definition in e.RemovedDefinitions)
                {
                    CatalogPart removedPart = null;
                    bool        removed     = false;

                    using (_lock.LockStateForRead())
                    {
                        removed = _activatedParts.TryGetValue(definition, out removedPart);
                    }

                    if (removed)
                    {
                        var capturedDefinition = definition;
                        DisposePart(null, removedPart, atomicComposition);
                        atomicComposition.AddCompleteActionAllowNull(() =>
                        {
                            using (_lock.LockStateForWrite())
                            {
                                _activatedParts.Remove(capturedDefinition);
                            }
                        });
                    }
                }

                UpdateRejections(addedExports.ConcatAllowingNull(removedExports), atomicComposition);

                OnExportsChanging(
                    new ExportsChangeEventArgs(addedExports, removedExports, atomicComposition));

                atomicComposition.AddCompleteAction(() => OnExportsChanged(
                                                        new ExportsChangeEventArgs(addedExports, removedExports, null)));

                atomicComposition.Complete();
            }
        }
コード例 #9
0
ファイル: ImportEngine.cs プロジェクト: zqb971/mef
        private CompositionResult TryRecomposeImports(PartManager partManager,
                                                      IEnumerable <ExportDefinition> changedExports, AtomicComposition atomicComposition)
        {
            var result = CompositionResult.SucceededResult;

            switch (partManager.State)
            {
            case ImportState.ImportsPreviewed:
            case ImportState.Composed:
                // Validate states to continue.
                break;

            default:
            {
                // All other states are invalid and for recomposition.
                return(new CompositionResult(ErrorBuilder.InvalidStateForRecompposition(partManager.Part)));
            }
            }

            var  affectedImports = RecompositionManager.GetAffectedImports(partManager.Part, changedExports);
            bool partComposed    = (partManager.State == ImportState.Composed);

            bool recomposedImport = false;

            foreach (var import in affectedImports)
            {
                result = result.MergeResult(
                    TryRecomposeImport(partManager, partComposed, import, atomicComposition));

                recomposedImport = true;
            }

            // Knowing that the part has already been composed before and that the only possible
            // changes are to recomposable imports, we can safely go ahead and do this now or
            // schedule it for later
            if (result.Succeeded && recomposedImport && partComposed)
            {
                if (atomicComposition == null)
                {
                    result = result.MergeResult(partManager.TryOnComposed());
                }
                else
                {
                    atomicComposition.AddCompleteAction(() => partManager.TryOnComposed().ThrowOnErrors());
                }
            }

            return(result);
        }
コード例 #10
0
ファイル: ImportEngine.cs プロジェクト: zqb971/mef
        private EngineContext GetEngineContext(AtomicComposition atomicComposition)
        {
            Assumes.NotNull(atomicComposition);

            EngineContext engineContext;

            if (!atomicComposition.TryGetValue(this, true, out engineContext))
            {
                EngineContext parentContext;
                atomicComposition.TryGetValue(this, false, out parentContext);
                engineContext = new EngineContext(this, parentContext);
                atomicComposition.SetValue(this, engineContext);
                atomicComposition.AddCompleteAction(engineContext.Complete);
            }
            return(engineContext);
        }
コード例 #11
0
        public void Complete_ShouldCopyActionsToInner()
        {
            bool executedAction = false;

            var innerAtomicComposition = new AtomicComposition();

            using (var ct = new AtomicComposition(innerAtomicComposition))
            {
                ct.AddCompleteAction(() => executedAction = true);

                ct.Complete();
                Assert.False(executedAction, "Action should not have been exectued yet");
            }

            innerAtomicComposition.Complete();
            Assert.True(executedAction);
        }
コード例 #12
0
ファイル: ImportEngine.cs プロジェクト: pgovind/runtime
        private EngineContext GetEngineContext(AtomicComposition atomicComposition)
        {
            if (atomicComposition == null)
            {
                throw new ArgumentNullException(nameof(atomicComposition));
            }

            EngineContext engineContext;

            if (!atomicComposition.TryGetValue(this, true, out engineContext))
            {
                EngineContext parentContext;
                atomicComposition.TryGetValue(this, false, out parentContext);
                engineContext = new EngineContext(this, parentContext);
                atomicComposition.SetValue(this, engineContext);
                atomicComposition.AddCompleteAction(engineContext.Complete);
            }
            return(engineContext);
        }
コード例 #13
0
ファイル: ImportEngine.cs プロジェクト: zqb971/mef
        private CompositionResult TryRecomposeImport(PartManager partManager, bool partComposed,
                                                     ImportDefinition import, AtomicComposition atomicComposition)
        {
            if (partComposed && !import.IsRecomposable)
            {
                return(new CompositionResult(ErrorBuilder.PreventedByExistingImport(partManager.Part, import)));
            }

            // During recomposition you must always requery with the new atomicComposition you cannot use any
            // cached value in the part manager
            var exportsResult = TryGetExports(this._sourceProvider, partManager.Part, import, atomicComposition);

            if (!exportsResult.Succeeded)
            {
                return(exportsResult.ToResult());
            }
            var exports = exportsResult.Value.AsArray();

            if (partComposed)
            {
                // Knowing that the part has already been composed before and that the only possible
                // changes are to recomposable imports, we can safely go ahead and do this now or
                // schedule it for later
                if (atomicComposition == null)
                {
                    return(partManager.TrySetImport(import, exports));
                }
                else
                {
                    atomicComposition.AddCompleteAction(() => partManager.TrySetImport(import, exports).ThrowOnErrors());
                }
            }
            else
            {
                partManager.SetSavedImport(import, exports, atomicComposition);
            }

            return(CompositionResult.SucceededResult);
        }
コード例 #14
0
        private void CopyComplete()
        {
            if (_outerAtomicComposition == null)
            {
                throw new Exception(SR.Diagnostic_InternalExceptionMessage);
            }

            _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);
            }
        }
コード例 #15
0
        private void UpdateRejections(IEnumerable <ExportDefinition> changedExports, AtomicComposition atomicComposition)
        {
            using (var localAtomicComposition = new AtomicComposition(atomicComposition))
            {
                // Reconsider every part definition that has been previously
                // rejected to see if any of them can be added back.
                var affectedRejections = new HashSet <ComposablePartDefinition>();

                ComposablePartDefinition[] rejectedParts;
                using (_lock.LockStateForRead())
                {
                    rejectedParts = _rejectedParts.ToArray();
                }
                foreach (var definition in rejectedParts)
                {
                    if (QueryPartState(localAtomicComposition, definition) == AtomicCompositionQueryState.TreatAsValidated)
                    {
                        continue;
                    }

                    foreach (var import in definition.ImportDefinitions.Where(ImportEngine.IsRequiredImportForPreview))
                    {
                        if (changedExports.Any(export => import.IsConstraintSatisfiedBy(export)))
                        {
                            affectedRejections.Add(definition);
                            break;
                        }
                    }
                }
                UpdateAtomicCompositionQueryForPartInHashSet(localAtomicComposition,
                                                             affectedRejections, AtomicCompositionQueryState.NeedsTesting);

                // Determine if any of the resurrectable parts is now available so that we can
                // notify listeners of the exact changes to exports
                var resurrectedExports = new List <ExportDefinition>();

                foreach (var partDefinition in affectedRejections)
                {
                    if (!IsRejected(partDefinition, localAtomicComposition))
                    {
                        // Notify listeners of the newly available exports and
                        // prepare to remove the rejected part from the list of rejections
                        resurrectedExports.AddRange(partDefinition.ExportDefinitions);

                        // Capture the local so that the closure below refers to the current definition
                        // in the loop and not the value of 'partDefinition' when the closure executes
                        var capturedPartDefinition = partDefinition;
                        localAtomicComposition.AddCompleteAction(() =>
                        {
                            using (_lock.LockStateForWrite())
                            {
                                _rejectedParts.Remove(capturedPartDefinition);
                            }

                            CompositionTrace.PartDefinitionResurrected(capturedPartDefinition);
                        });
                    }
                }

                // Notify anyone sourcing exports that the resurrected exports have appeared
                if (resurrectedExports.Any())
                {
                    OnExportsChanging(
                        new ExportsChangeEventArgs(resurrectedExports, Array.Empty <ExportDefinition>(), localAtomicComposition));

                    localAtomicComposition.AddCompleteAction(() => OnExportsChanged(
                                                                 new ExportsChangeEventArgs(resurrectedExports, Array.Empty <ExportDefinition>(), null)));
                }

                localAtomicComposition.Complete();
            }
        }
コード例 #16
0
ファイル: CatalogExportProvider.cs プロジェクト: keutmann/SPM
        private void UpdateRejections(IEnumerable <ExportDefinition> changedExports, AtomicComposition atomicComposition)
        {
            using (var localAtomicComposition = new AtomicComposition(atomicComposition))
            {
                // Reconsider every part definition that has been previously
                // rejected to see if any of them can be added back.
                var affectedRejections     = new HashSet <ComposablePartDefinition>();
                var atomicCompositionQuery = GetAtomicCompositionQuery(localAtomicComposition);

                ComposablePartDefinition[] rejectedParts;
                using (new ReadLock(this._lock))
                {
                    rejectedParts = this._rejectedParts.ToArray();
                }
                foreach (var definition in rejectedParts)
                {
                    if (atomicCompositionQuery(definition) == AtomicCompositionQueryState.TreatAsValidated)
                    {
                        continue;
                    }

                    foreach (var import in definition.ImportDefinitions.Where(ImportEngine.IsRequiredImportForPreview))
                    {
                        if (changedExports.Any(export => import.IsConstraintSatisfiedBy(export)))
                        {
                            affectedRejections.Add(definition);
                            break;
                        }
                    }
                }
                UpdateAtomicCompositionQuery(localAtomicComposition,
                                             def => affectedRejections.Contains(def), AtomicCompositionQueryState.NeedsTesting);

                // Determine if any of the resurrectable parts is now available so that we can
                // notify listeners of the exact changes to exports
                var resurrectedExports = new List <ExportDefinition>();

                foreach (var partDefinition in affectedRejections)
                {
                    if (!IsRejected(partDefinition, localAtomicComposition))
                    {
                        // Notify listeners of the newly available exports and
                        // prepare to remove the rejected part from the list of rejections
                        resurrectedExports.AddRange(partDefinition.ExportDefinitions);
                        localAtomicComposition.AddCompleteAction(() =>
                        {
                            using (new WriteLock(this._lock))
                            {
                                this._rejectedParts.Remove(partDefinition);
                            }
                        });
                    }
                }

                // Notify anyone sourcing exports that the resurrected exports have appeared
                if (resurrectedExports.Any())
                {
                    this.OnExportsChanging(
                        new ExportsChangeEventArgs(resurrectedExports, new ExportDefinition[0], localAtomicComposition));

                    localAtomicComposition.AddCompleteAction(() => this.OnExportsChanged(
                                                                 new ExportsChangeEventArgs(resurrectedExports, new ExportDefinition[0], null)));
                }

                localAtomicComposition.Complete();
            }
        }