Exemplo n.º 1
0
        /// <summary>
        ///     Creates a source block that produces a transformed value for each value from original source block,
        ///     skipping intermediate input and output states, and hence is not suitable for producing or consuming
        ///     deltas.
        /// </summary>
        /// <typeparam name="TOut">
        ///     The type of value produced by <paramref name="transform"/>.
        ///  </typeparam>
        /// <param name="source">
        ///     The source block whose values are to be transformed.
        /// </param>
        /// <param name="transform">
        ///     The function to execute on each value from <paramref name="source"/>.
        /// </param>
        /// <param name="suppressVersionOnlyUpdates">
        ///     A value indicating whether to prevent messages from propagating to the target
        ///     block if no project changes are include other than an incremented version number.
        /// </param>
        /// <param name="ruleNames">
        ///     The names of the rules that describe the project data the caller is interested in.
        /// </param>
        /// <returns>
        ///     The transformed source block and a disposable value that terminates the link.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="source"/> is <see langword="null"/>.
        ///     <para>
        ///         -or-
        ///     </para>
        ///     <paramref name="transform"/> is <see langword="null"/>.
        /// </exception>
        public static DisposableValue <ISourceBlock <TOut> > TransformWithNoDelta <TOut>(
            this ISourceBlock <IProjectVersionedValue <IProjectSubscriptionUpdate> > source,
            Func <IProjectVersionedValue <IProjectSubscriptionUpdate>, TOut> transform,
            bool suppressVersionOnlyUpdates,
            IEnumerable <string>?ruleNames = null)
        {
            Requires.NotNull(source, nameof(source));
            Requires.NotNull(transform, nameof(transform));

            IPropagatorBlock <IProjectVersionedValue <IProjectSubscriptionUpdate>, TOut> transformBlock = DataflowBlockSlim.CreateTransformBlock(transform, skipIntermediateInputData: true, skipIntermediateOutputData: true);

            IDisposable link = source.LinkTo(transformBlock,
                                             DataflowOption.PropagateCompletion,
                                             initialDataAsNew: true,
                                             suppressVersionOnlyUpdates: suppressVersionOnlyUpdates,
                                             ruleNames: ruleNames);

            return(new DisposableValue <ISourceBlock <TOut> >(transformBlock, link));
        }
        protected override IDisposable LinkExternalInput(ITargetBlock <IProjectVersionedValue <UpToDateCheckImplicitConfiguredInput> > targetBlock)
        {
            Assumes.Present(_configuredProject.Services.ProjectSubscription);

            bool attemptedStateRestore = false;

            // Initial state is empty. We will evolve this reference over time, updating it iteratively
            // on each new data update.
            UpToDateCheckImplicitConfiguredInput state = UpToDateCheckImplicitConfiguredInput.CreateEmpty(_configuredProject.ProjectConfiguration);

            IPropagatorBlock <IProjectVersionedValue <UpdateValues>, IProjectVersionedValue <UpToDateCheckImplicitConfiguredInput> > transformBlock
                = DataflowBlockSlim.CreateTransformBlock <IProjectVersionedValue <UpdateValues>, IProjectVersionedValue <UpToDateCheckImplicitConfiguredInput> >(TransformAsync);

            IProjectValueDataSource <IProjectSubscriptionUpdate> source1 = _configuredProject.Services.ProjectSubscription.JointRuleSource;
            IProjectValueDataSource <IProjectSubscriptionUpdate> source2 = _configuredProject.Services.ProjectSubscription.SourceItemsRuleSource;
            IProjectItemSchemaService source3 = _projectItemSchemaService;
            IProjectValueDataSource <IProjectCatalogSnapshot> source4 = _configuredProject.Services.ProjectSubscription.ProjectCatalogSource;

            return(new DisposableBag
            {
                // Sync-link various sources to our transform block
                ProjectDataSources.SyncLinkTo(
                    source1.SourceBlock.SyncLinkOptions(DataflowOption.WithRuleNames(ProjectPropertiesSchemas)),
                    source2.SourceBlock.SyncLinkOptions(),
                    source3.SourceBlock.SyncLinkOptions(),
                    source4.SourceBlock.SyncLinkOptions(),
                    target: transformBlock,
                    linkOptions: DataflowOption.PropagateCompletion,
                    CancellationToken.None),

                // Link the transform block to our target block
                transformBlock.LinkTo(targetBlock, DataflowOption.PropagateCompletion),

                JoinUpstreamDataSources(source1, source2, source3, source4)
            });

            async Task <IProjectVersionedValue <UpToDateCheckImplicitConfiguredInput> > TransformAsync(IProjectVersionedValue <UpdateValues> e)
            {
                if (!attemptedStateRestore)
                {
                    attemptedStateRestore = true;

                    if (_persistentState is not null)
                    {
                        // Restoring state requires the UI thread. We must use JTF.RunAsync here to ensure the UI
                        // thread is shared between related work and prevent deadlocks.
                        (int ItemHash, DateTime InputsChangedAtUtc)? restoredState =
                            await JoinableFactory.RunAsync(() => _persistentState.RestoreStateAsync(_configuredProject.UnconfiguredProject.FullPath, _configuredProject.ProjectConfiguration.Dimensions));

                        if (restoredState is not null)
                        {
                            state = state.WithRestoredState(restoredState.Value.ItemHash, restoredState.Value.InputsChangedAtUtc);
                        }
                    }
                }

                int?     priorItemHash = state.ItemHash;
                DateTime priorLastItemsChangedAtUtc = state.LastItemsChangedAtUtc;

                state = state.Update(
                    jointRuleUpdate: e.Value.Item1,
                    sourceItemsUpdate: e.Value.Item2,
                    projectItemSchema: e.Value.Item3,
                    projectCatalogSnapshot: e.Value.Item4);

                if (state.ItemHash is not null && _persistentState is not null && (priorItemHash != state.ItemHash || priorLastItemsChangedAtUtc != state.LastItemsChangedAtUtc))
                {
                    _persistentState.StoreState(_configuredProject.UnconfiguredProject.FullPath, _configuredProject.ProjectConfiguration.Dimensions, state.ItemHash.Value, state.LastItemsChangedAtUtc);
                }

                return(new ProjectVersionedValue <UpToDateCheckImplicitConfiguredInput>(state, e.DataSourceVersions));
            }
        }
Exemplo n.º 3
0
        /// <summary>
        ///     Creates a source block that produces a transformed value for each value from original source block,
        ///     skipping intermediate input and output states, and hence is not suitable for producing or consuming
        ///     deltas.
        /// </summary>
        /// <typeparam name="TInput">
        ///     The type of the input value produced by <paramref name="source"/>.
        /// </typeparam>
        /// <typeparam name="TOut">
        ///     The type of value produced by <paramref name="transform"/>.
        ///  </typeparam>
        /// <param name="source">
        ///     The source block whose values are to be transformed.
        /// </param>
        /// <param name="transform">
        ///     The function to execute on each value from <paramref name="source"/>.
        /// </param>
        /// <returns>
        ///     The transformed source block and a disposable value that terminates the link.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="source"/> is <see langword="null"/>.
        ///     <para>
        ///         -or-
        ///     </para>
        ///     <paramref name="transform"/> is <see langword="null"/>.
        /// </exception>
        public static DisposableValue <ISourceBlock <TOut> > TransformWithNoDelta <TInput, TOut>(
            this ISourceBlock <IProjectVersionedValue <TInput> > source,
            Func <IProjectVersionedValue <TInput>, TOut> transform)
        {
            Requires.NotNull(source, nameof(source));
            Requires.NotNull(transform, nameof(transform));

            IPropagatorBlock <IProjectVersionedValue <TInput>, TOut> transformBlock = DataflowBlockSlim.CreateTransformBlock(transform, skipIntermediateInputData: true, skipIntermediateOutputData: true);

            IDisposable link = source.LinkTo(transformBlock, DataflowOption.PropagateCompletion);

            return(new DisposableValue <ISourceBlock <TOut> >(transformBlock, link));
        }
        public IDisposable RegisterSnapshotProvider(DependenciesSnapshotProvider snapshotProvider)
        {
            Requires.NotNull(snapshotProvider, nameof(snapshotProvider));

            var unregister = new DisposableBag();

            lock (_lock)
            {
                snapshotProvider.SnapshotRenamed           += OnSnapshotRenamed;
                snapshotProvider.SnapshotProviderUnloading += OnSnapshotProviderUnloading;

                ITargetBlock <SnapshotChangedEventArgs> actionBlock = DataflowBlockSlim.CreateActionBlock <SnapshotChangedEventArgs>(
                    e => SnapshotChanged?.Invoke(this, e),
                    "AggregateDependenciesSnapshotProviderSource {1}",
                    skipIntermediateInputData: true);

                unregister.Add(
                    snapshotProvider.SnapshotChangedSource.LinkTo(
                        actionBlock,
                        DataflowOption.PropagateCompletion));

                _snapshotProviderByPath = _snapshotProviderByPath.SetItem(snapshotProvider.CurrentSnapshot.ProjectPath, snapshotProvider);
            }

            unregister.Add(new DisposableDelegate(
                               () =>
            {
                lock (_lock)
                {
                    string projectPath                          = snapshotProvider.CurrentSnapshot.ProjectPath;
                    _snapshotProviderByPath                     = _snapshotProviderByPath.Remove(projectPath);
                    snapshotProvider.SnapshotRenamed           -= OnSnapshotRenamed;
                    snapshotProvider.SnapshotProviderUnloading -= OnSnapshotProviderUnloading;
                }
            }));

            return(unregister);

            void OnSnapshotProviderUnloading(object sender, SnapshotProviderUnloadingEventArgs e)
            {
                // Project has unloaded, so remove it from the cache and unregister event handlers
                SnapshotProviderUnloading?.Invoke(this, e);

                unregister.Dispose();
            }

            void OnSnapshotRenamed(object sender, ProjectRenamedEventArgs e)
            {
                if (string.IsNullOrEmpty(e.OldFullPath))
                {
                    return;
                }

                lock (_lock)
                {
                    // Remove and re-add provider with new project path
                    if (_snapshotProviderByPath.TryGetValue(e.OldFullPath, out DependenciesSnapshotProvider provider))
                    {
                        _snapshotProviderByPath = _snapshotProviderByPath.Remove(e.OldFullPath);

                        if (!string.IsNullOrEmpty(e.NewFullPath))
                        {
                            _snapshotProviderByPath = _snapshotProviderByPath.SetItem(e.NewFullPath, provider);
                        }
                    }
                }
            }
        }
Exemplo n.º 5
0
        protected override void Initialize()
        {
            base.Initialize();

            _broadcastBlock = DataflowBlockSlim.CreateBroadcastBlock <IProjectVersionedValue <T> >(null);
        }
        /// <summary>
        /// Generates the original references directory tree.
        /// </summary>
        protected override void Initialize()
        {
#pragma warning disable RS0030 // symbol LoadedProject is banned
            using (UnconfiguredProjectAsynchronousTasksService.LoadedProject())
            {
                base.Initialize();

                // this.IsApplicable may take a project lock, so we can't do it inline with this method
                // which is holding a private lock.  It turns out that doing it asynchronously isn't a problem anyway,
                // so long as we guard against races with the Dispose method.
                UnconfiguredProjectAsynchronousTasksService.LoadedProjectAsync(
                    async delegate
                {
                    await TaskScheduler.Default.SwitchTo(alwaysYield: true);
                    UnconfiguredProjectAsynchronousTasksService
                    .UnloadCancellationToken.ThrowIfCancellationRequested();

                    lock (SyncObject)
                    {
                        Verify.NotDisposed(this);

                        // Issue this token before hooking the SnapshotChanged event to prevent a race
                        // where a snapshot tree is replaced by the initial, empty tree created below.
                        // The handler will cancel this token before submitting its update.
                        CancellationToken initialTreeCancellationToken = _treeUpdateCancellationSeries.CreateNext();

                        _ = SubmitTreeUpdateAsync(
                            delegate
                        {
                            IProjectTree dependenciesNode = CreateDependenciesFolder();

                            return(Task.FromResult(new TreeUpdateResult(dependenciesNode)));
                        },
                            initialTreeCancellationToken);

                        ITargetBlock <SnapshotChangedEventArgs> actionBlock = DataflowBlockSlim.CreateActionBlock <SnapshotChangedEventArgs>(
                            e => OnDependenciesSnapshotChanged(_dependenciesSnapshotProvider, e),
                            "DependenciesProjectTreeProviderSource {1}",
                            skipIntermediateInputData: true);
                        _snapshotEventListener = _dependenciesSnapshotProvider.SnapshotChangedSource.LinkTo(actionBlock, new DataflowLinkOptions()
                        {
                            PropagateCompletion = true
                        });
                    }
                },
                    registerFaultHandler: true);
            }
#pragma warning restore RS0030 // symbol LoadedProject is banned

            IProjectTree CreateDependenciesFolder()
            {
                var values = new ReferencesProjectTreeCustomizablePropertyValues
                {
                    Caption      = Resources.DependenciesNodeName,
                    Icon         = ManagedImageMonikers.ReferenceGroup.ToProjectSystemType(),
                    ExpandedIcon = ManagedImageMonikers.ReferenceGroup.ToProjectSystemType(),
                    Flags        = DependencyTreeFlags.DependenciesRootNodeFlags
                };

                // Allow property providers to perform customization.
                // These are ordered from lowest priority to highest, allowing higher priority
                // providers to override lower priority providers.
                foreach (IProjectTreePropertiesProvider provider in _projectTreePropertiesProviders.ExtensionValues())
                {
                    provider.CalculatePropertyValues(null, values);
                }

                // Note that all the parameters are specified so we can force this call to an
                // overload of NewTree available prior to 15.5 versions of CPS. Once a 15.5 build
                // is publicly available we can move this to an overload with default values for
                // most of the parameters, and we'll only need to pass the interesting ones.
                return(NewTree(
                           caption: values.Caption,
                           filePath: null,
                           browseObjectProperties: null,
                           icon: values.Icon,
                           expandedIcon: values.ExpandedIcon,
                           visible: true,
                           flags: values.Flags));
            }
        }
Exemplo n.º 7
0
        protected override void Initialize()
        {
            // Create our broadcast block for subscribers to get new ILaunchProfiles Information
            _broadcastBlock     = DataflowBlockSlim.CreateBroadcastBlock <ILaunchSettings>();
            _changedSourceBlock = _broadcastBlock.SafePublicize();


            // Subscribe to changes to the broadcast block using the idle scheduler. This should filter out a lot of the intermediates
            // states that files can be in.
            if (_projectSubscriptionService != null)
            {
                // The use of AsyncLazy with dataflow can allow state stored in the execution context to leak through. The downstream affect is
                // calls to say, get properties, may fail. To avoid this, we capture the execution context here, and it will be reapplied when
                // we get new subscription data from the dataflow.
                ITargetBlock <IProjectVersionedValue <Tuple <IProjectSubscriptionUpdate, IProjectCapabilitiesSnapshot> > > projectChangesBlock = DataflowBlockSlim.CreateActionBlock(
                    DataflowUtilities.CaptureAndApplyExecutionContext <IProjectVersionedValue <Tuple <IProjectSubscriptionUpdate, IProjectCapabilitiesSnapshot> > >(ProjectRuleBlock_ChangedAsync));
                StandardRuleDataflowLinkOptions evaluationLinkOptions = DataflowOption.WithRuleNames(ProjectDebugger.SchemaName);

                _projectRuleSubscriptionLink = ProjectDataSources.SyncLinkTo(
                    _projectSubscriptionService.ProjectRuleSource.SourceBlock.SyncLinkOptions(evaluationLinkOptions),
                    _commonProjectServices.Project.Capabilities.SourceBlock.SyncLinkOptions(),
                    projectChangesBlock,
                    linkOptions: DataflowOption.PropagateCompletion);
            }

            // Make sure we are watching the file at this point
            WatchLaunchSettingsFile();
        }
Exemplo n.º 8
0
        protected override void Initialize()
        {
            IPropagatorBlock <IProjectVersionedValue <ILaunchSettings>, IProjectVersionedValue <IReadOnlyList <IEnumValue> > > debugProfilesBlock = DataflowBlockSlim.CreateTransformBlock <IProjectVersionedValue <ILaunchSettings>, IProjectVersionedValue <IReadOnlyList <IEnumValue> > >(
                update =>
            {
                // Compute the new enum values from the profile provider
                var generatedResult = DebugProfileEnumValuesGenerator.GetEnumeratorEnumValues(update.Value).ToImmutableList();
                _dataSourceVersion++;
                ImmutableDictionary <NamedIdentity, IComparable> dataSources = ImmutableDictionary <NamedIdentity, IComparable> .Empty.Add(DataSourceKey, DataSourceVersion);
                return(new ProjectVersionedValue <IReadOnlyList <IEnumValue> >(generatedResult, dataSources));
            });

            IBroadcastBlock <IProjectVersionedValue <IReadOnlyList <IEnumValue> > > broadcastBlock = DataflowBlockSlim.CreateBroadcastBlock <IProjectVersionedValue <IReadOnlyList <IEnumValue> > >();

            // The interface has two definitions of SourceBlock: one from
            // ILaunchSettingsProvider, and one from IProjectValueDataSource<T> (via
            // IVersionedLaunchSettingsProvider). We need the cast to pick the proper one.
            _launchProfileProviderLink = ((IProjectValueDataSource <ILaunchSettings>)LaunchSettingProvider).SourceBlock.LinkTo(
                debugProfilesBlock,
                linkOptions: DataflowOption.PropagateCompletion);

            JoinUpstreamDataSources(LaunchSettingProvider);

            _debugProviderLink = debugProfilesBlock.LinkTo(broadcastBlock, DataflowOption.PropagateCompletion);

            _publicBlock = broadcastBlock.SafePublicize();
        }
Exemplo n.º 9
0
        private void SubscribeToConfiguredProject(
            ConfiguredProject configuredProject,
            IProjectSubscriptionService subscriptionService,
            IReadOnlyCollection <string> watchedEvaluationRules,
            IReadOnlyCollection <string> watchedDesignTimeBuildRules)
        {
            // Use intermediate buffer blocks for project rule data to allow subsequent blocks
            // to only observe specific rule name(s).

            var intermediateBlockDesignTime =
                new BufferBlock <IProjectVersionedValue <IProjectSubscriptionUpdate> >(
                    new ExecutionDataflowBlockOptions()
            {
                NameFormat = "CrossTarget Intermediate DesignTime Input: {1}"
            });

            var intermediateBlockEvaluation =
                new BufferBlock <IProjectVersionedValue <IProjectSubscriptionUpdate> >(
                    new ExecutionDataflowBlockOptions()
            {
                NameFormat = "CrossTarget Intermediate Evaluation Input: {1}"
            });

            _subscriptions ??= new DisposableBag();

            _subscriptions.AddDisposable(
                subscriptionService.JointRuleSource.SourceBlock.LinkTo(
                    intermediateBlockDesignTime,
                    ruleNames: watchedDesignTimeBuildRules.Union(watchedEvaluationRules),
                    suppressVersionOnlyUpdates: true,
                    linkOptions: DataflowOption.PropagateCompletion));

            _subscriptions.AddDisposable(
                subscriptionService.ProjectRuleSource.SourceBlock.LinkTo(
                    intermediateBlockEvaluation,
                    ruleNames: watchedEvaluationRules,
                    suppressVersionOnlyUpdates: true,
                    linkOptions: DataflowOption.PropagateCompletion));

            ITargetBlock <IProjectVersionedValue <Tuple <IProjectSubscriptionUpdate, IProjectCatalogSnapshot, IProjectCapabilitiesSnapshot> > > actionBlockDesignTimeBuild =
                DataflowBlockSlim.CreateActionBlock <IProjectVersionedValue <Tuple <IProjectSubscriptionUpdate, IProjectCatalogSnapshot, IProjectCapabilitiesSnapshot> > >(
                    e => OnProjectChangedAsync(e.Value.Item1, e.Value.Item2, e.Value.Item3, configuredProject, RuleHandlerType.DesignTimeBuild),
                    new ExecutionDataflowBlockOptions()
            {
                NameFormat = "CrossTarget DesignTime Input: {1}"
            });

            ITargetBlock <IProjectVersionedValue <Tuple <IProjectSubscriptionUpdate, IProjectCatalogSnapshot, IProjectCapabilitiesSnapshot> > > actionBlockEvaluation =
                DataflowBlockSlim.CreateActionBlock <IProjectVersionedValue <Tuple <IProjectSubscriptionUpdate, IProjectCatalogSnapshot, IProjectCapabilitiesSnapshot> > >(
                    e => OnProjectChangedAsync(e.Value.Item1, e.Value.Item2, e.Value.Item3, configuredProject, RuleHandlerType.Evaluation),
                    new ExecutionDataflowBlockOptions()
            {
                NameFormat = "CrossTarget Evaluation Input: {1}"
            });

            _subscriptions.AddDisposable(ProjectDataSources.SyncLinkTo(
                                             intermediateBlockDesignTime.SyncLinkOptions(),
                                             subscriptionService.ProjectCatalogSource.SourceBlock.SyncLinkOptions(),
                                             configuredProject.Capabilities.SourceBlock.SyncLinkOptions(),
                                             actionBlockDesignTimeBuild,
                                             linkOptions: DataflowOption.PropagateCompletion));

            _subscriptions.AddDisposable(ProjectDataSources.SyncLinkTo(
                                             intermediateBlockEvaluation.SyncLinkOptions(),
                                             subscriptionService.ProjectCatalogSource.SourceBlock.SyncLinkOptions(),
                                             configuredProject.Capabilities.SourceBlock.SyncLinkOptions(),
                                             actionBlockEvaluation,
                                             linkOptions: DataflowOption.PropagateCompletion));
        }
        protected override void Initialize()
        {
            base.Initialize();

            // Create an action block to process the design time inputs and configuration general changes
            ITargetBlock <IProjectVersionedValue <Tuple <DesignTimeInputs, IProjectSubscriptionUpdate> > > inputsAction = DataflowBlockSlim.CreateActionBlock <IProjectVersionedValue <Tuple <DesignTimeInputs, IProjectSubscriptionUpdate> > >(ProcessDataflowChanges);

            _broadcastBlock = DataflowBlockSlim.CreateBroadcastBlock <IProjectVersionedValue <DesignTimeInputsDelta> >(nameFormat: nameof(DesignTimeInputsChangeTracker) + "Broadcast {1}");
            _publicBlock    = AllowSourceBlockCompletion ? _broadcastBlock : _broadcastBlock.SafePublicize();

            IDisposable projectLink = ProjectDataSources.SyncLinkTo(
                _inputsDataSource.SourceBlock.SyncLinkOptions(
                    linkOptions: DataflowOption.PropagateCompletion),
                _projectSubscriptionService.ProjectRuleSource.SourceBlock.SyncLinkOptions(
                    linkOptions: DataflowOption.WithRuleNames(ConfigurationGeneral.SchemaName)),
                inputsAction,
                DataflowOption.PropagateCompletion,
                cancellationToken: _project.Services.ProjectAsynchronousTasks.UnloadCancellationToken);

            // Create an action block to process file change notifications
            ITargetBlock <IProjectVersionedValue <string[]> > fileWatcherAction = DataflowBlockSlim.CreateActionBlock <IProjectVersionedValue <string[]> >(ProcessFileChangeNotification);
            IDisposable watcherLink = _fileWatcher.SourceBlock.LinkTo(fileWatcherAction, DataflowOption.PropagateCompletion);

            _disposables.Add(projectLink);
            _disposables.Add(watcherLink);

            JoinUpstreamDataSources(_inputsDataSource, _projectSubscriptionService.ProjectRuleSource, _fileWatcher);
        }