@@ -30,27 +30,31 @@ internal sealed class CompositeSource : IDataSource, ICompositeSourceActionable
3030
3131 private readonly IDataSourceUpdatesV2 _originalUpdateSink ;
3232 private readonly IDataSourceUpdatesV2 _sanitizedUpdateSink ;
33- private readonly SourcesList < ( SourceFactory Factory , ActionApplierFactory ActionApplierFactory ) > _sourcesList ;
33+ private readonly SourcesList < ( SourceFactory Factory , ActionApplierFactory ActionApplierFactory , CompositeEntryKind Kind ) > _sourcesList ;
3434 private readonly DisableableDataSourceUpdatesTracker _disableableTracker ;
3535
3636 // Tracks the entry from the sources list that was used to create the current
3737 // data source instance. This allows operations such as blacklist to remove
3838 // the correct factory/action-applier-factory tuple from the list.
39- private ( SourceFactory Factory , ActionApplierFactory ActionApplierFactory ) _currentEntry ;
39+ private ( SourceFactory Factory , ActionApplierFactory ActionApplierFactory , CompositeEntryKind Kind ) _currentEntry ;
4040 private IDataSource _currentDataSource ;
4141
4242 /// <summary>
43- /// Creates a new <see cref="CompositeSource"/>.
43+ /// Creates a new <see cref="CompositeSource"/>. Each entry carries an explicit
44+ /// <see cref="CompositeEntryKind"/> so appliers can express "block every FDv2 entry"
45+ /// via <see cref="ICompositeSourceActionable.BlockAll"/> without needing to know list
46+ /// positions. For inner sub-composites whose entries never participate in cross-kind
47+ /// blocking, callers should pass <see cref="CompositeEntryKind.FDv2"/> uniformly.
4448 /// </summary>
4549 /// <param name="compositeDescription">description of the composite source for logging purposes</param>
4650 /// <param name="updatesSink">the sink that receives updates from the active source</param>
47- /// <param name="factoryTuples">the ordered list of source factories and their associated action applier factories</param>
51+ /// <param name="factoryTuples">the ordered list of source factories, action applier factories, and entry kinds </param>
4852 /// <param name="logger">the logger instance to use</param>
4953 /// <param name="circular">whether to loop off the end of the list back to the start when fallback occurs</param>
5054 public CompositeSource (
5155 string compositeDescription ,
5256 IDataSourceUpdatesV2 updatesSink ,
53- IList < ( SourceFactory Factory , ActionApplierFactory ActionApplierFactory ) > factoryTuples ,
57+ IList < ( SourceFactory Factory , ActionApplierFactory ActionApplierFactory , CompositeEntryKind Kind ) > factoryTuples ,
5458 Logger logger ,
5559 bool circular = true )
5660 {
@@ -72,7 +76,7 @@ public CompositeSource(
7276 // this tracker is used to disconnect the current source from the updates sink when it is no longer needed.
7377 _disableableTracker = new DisableableDataSourceUpdatesTracker ( ) ;
7478
75- _sourcesList = new SourcesList < ( SourceFactory SourceFactory , ActionApplierFactory ActionApplierFactory ) > (
79+ _sourcesList = new SourcesList < ( SourceFactory Factory , ActionApplierFactory ActionApplierFactory , CompositeEntryKind Kind ) > (
7680 circular : circular ,
7781 initialList : factoryTuples
7882 ) ;
@@ -451,6 +455,38 @@ public void BlockCurrent()
451455 } ) ;
452456 }
453457
458+ public void BlockAll ( Predicate < CompositeEntryKind > kindMatches )
459+ {
460+ if ( kindMatches is null ) throw new ArgumentNullException ( nameof ( kindMatches ) ) ;
461+ if ( _disposed )
462+ {
463+ return ;
464+ }
465+
466+ EnqueueAction ( ( ) =>
467+ {
468+ lock ( _lock )
469+ {
470+ var removed = _sourcesList . RemoveAll ( entry => kindMatches ( entry . Kind ) ) ;
471+ if ( removed == 0 )
472+ {
473+ return ;
474+ }
475+
476+ // If the current entry was removed, clear the reference so a subsequent
477+ // BlockCurrent doesn't accidentally remove a remaining entry. The current
478+ // data source is left running -- callers are expected to follow BlockAll
479+ // with DisposeCurrent / GoToNext when they want it torn down.
480+ if ( _currentEntry != default && kindMatches ( _currentEntry . Kind ) )
481+ {
482+ _currentEntry = default ;
483+ }
484+
485+ _log . Debug ( "{0} blocked {1} entries by kind predicate." , _compositeDescription , removed ) ;
486+ }
487+ } ) ;
488+ }
489+
454490 private void logTransition ( String previousDescription , String currentDescription ) {
455491 if ( previousDescription != null && currentDescription != null ) {
456492 _log . Debug ( "{0} transitioned from {1} to {2}." , _compositeDescription , previousDescription , currentDescription ) ;
0 commit comments