@@ -28,8 +28,6 @@ internal sealed partial class EventHubListener
2828 /// </summary>
2929 internal class PartitionProcessor : IEventProcessor , IDisposable
3030 {
31- private readonly CancellationTokenSource _cts = new ( ) ;
32-
3331 private readonly ITriggeredFunctionExecutor _executor ;
3432 private readonly bool _singleDispatch ;
3533 private readonly ILogger _logger ;
@@ -44,13 +42,15 @@ internal class PartitionProcessor : IEventProcessor, IDisposable
4442 private Task _cachedEventsBackgroundTask ;
4543 private CancellationTokenSource _cachedEventsBackgroundTaskCts ;
4644 private SemaphoreSlim _cachedEventsGuard ;
45+ private readonly CancellationToken _functionExecutionToken ;
46+ private readonly CancellationTokenSource _ownershipLostTokenSource ;
4747
4848 /// <summary>
4949 /// When we have a minimum batch size greater than 1, this class manages caching events.
5050 /// </summary>
5151 internal PartitionProcessorEventsManager CachedEventsManager { get ; }
5252
53- public PartitionProcessor ( EventHubOptions options , ITriggeredFunctionExecutor executor , ILogger logger , bool singleDispatch )
53+ public PartitionProcessor ( EventHubOptions options , ITriggeredFunctionExecutor executor , ILogger logger , bool singleDispatch , CancellationToken functionExecutionToken )
5454 {
5555 _executor = executor ;
5656 _singleDispatch = singleDispatch ;
@@ -59,6 +59,8 @@ public PartitionProcessor(EventHubOptions options, ITriggeredFunctionExecutor ex
5959 _firstFunctionInvocation = true ;
6060 _maxWaitTime = options . MaxWaitTime ;
6161 _minimumBatchesEnabled = options . MinEventBatchSize > 1 ; // 1 is the default
62+ _functionExecutionToken = functionExecutionToken ;
63+ _ownershipLostTokenSource = new CancellationTokenSource ( ) ;
6264
6365 // Events are only cached when building a batch of minimum size.
6466 if ( _minimumBatchesEnabled )
@@ -70,8 +72,12 @@ public PartitionProcessor(EventHubOptions options, ITriggeredFunctionExecutor ex
7072
7173 public Task CloseAsync ( EventProcessorHostPartition context , ProcessingStoppedReason reason )
7274 {
73- // signal cancellation for any in progress executions and clear the cached events
74- _cts . Cancel ( ) ;
75+ if ( reason == ProcessingStoppedReason . OwnershipLost )
76+ {
77+ _ownershipLostTokenSource . Cancel ( ) ;
78+ }
79+
80+ // clear the cached events
7581 CachedEventsManager ? . ClearEventCache ( ) ;
7682
7783 _logger . LogDebug ( GetOperationDetails ( context , $ "CloseAsync, { reason } ") ) ;
@@ -98,11 +104,10 @@ public Task ProcessErrorAsync(EventProcessorHostPartition context, Exception err
98104 /// </summary>
99105 /// <param name="context">The partition information for this partition.</param>
100106 /// <param name="messages">The events to process.</param>
101- /// <param name="partitionProcessingCancellationToken">The cancellation token to respect if processing for the partition is canceled.</param>
102107 /// <returns></returns>
103- public async Task ProcessEventsAsync ( EventProcessorHostPartition context , IEnumerable < EventData > messages , CancellationToken partitionProcessingCancellationToken )
108+ public async Task ProcessEventsAsync ( EventProcessorHostPartition context , IEnumerable < EventData > messages )
104109 {
105- using CancellationTokenSource linkedCts = CancellationTokenSource . CreateLinkedTokenSource ( _cts . Token , partitionProcessingCancellationToken ) ;
110+ using CancellationTokenSource linkedCts = CancellationTokenSource . CreateLinkedTokenSource ( _functionExecutionToken , _ownershipLostTokenSource . Token ) ;
106111 _mostRecentPartitionContext = context ;
107112 var events = messages . ToArray ( ) ;
108113 EventData eventToCheckpoint = null ;
@@ -135,7 +140,7 @@ public async Task ProcessEventsAsync(EventProcessorHostPartition context, IEnume
135140 TriggerDetails = eventHubTriggerInput . GetTriggerDetails ( context )
136141 } ;
137142
138- await _executor . TryExecuteAsync ( input , linkedCts . Token ) . ConfigureAwait ( false ) ;
143+ await _executor . TryExecuteAsync ( input , _functionExecutionToken ) . ConfigureAwait ( false ) ;
139144 _firstFunctionInvocation = false ;
140145 eventToCheckpoint = events [ i ] ;
141146 }
@@ -168,7 +173,7 @@ public async Task ProcessEventsAsync(EventProcessorHostPartition context, IEnume
168173 _logger . LogDebug ( $ "Partition Processor received events and is attempting to invoke function ({ details } )") ;
169174
170175 UpdateCheckpointContext ( triggerEvents , context ) ;
171- await TriggerExecute ( triggerEvents , context , linkedCts . Token ) . ConfigureAwait ( false ) ;
176+ await TriggerExecute ( triggerEvents , context , _functionExecutionToken ) . ConfigureAwait ( false ) ;
172177 eventToCheckpoint = triggerEvents . Last ( ) ;
173178
174179 // If there is a background timer task, cancel it and dispose of the cancellation token. If there
@@ -186,7 +191,8 @@ public async Task ProcessEventsAsync(EventProcessorHostPartition context, IEnume
186191 if ( _cachedEventsBackgroundTaskCts == null && CachedEventsManager . HasCachedEvents )
187192 {
188193 // If there are events waiting to be processed, and no background task running, start a monitoring cycle.
189- _cachedEventsBackgroundTaskCts = CancellationTokenSource . CreateLinkedTokenSource ( _cts . Token ) ;
194+ // Don't reference linkedCts in the class level background task, as it will be disposed when the method goes out of scope.
195+ _cachedEventsBackgroundTaskCts = CancellationTokenSource . CreateLinkedTokenSource ( _functionExecutionToken , _ownershipLostTokenSource . Token ) ;
190196 _cachedEventsBackgroundTask = MonitorCachedEvents ( context . ProcessorHost . GetLastReadCheckpoint ( context . PartitionId ) ? . LastModified , _cachedEventsBackgroundTaskCts ) ;
191197 }
192198 }
@@ -201,7 +207,7 @@ public async Task ProcessEventsAsync(EventProcessorHostPartition context, IEnume
201207 else
202208 {
203209 UpdateCheckpointContext ( events , context ) ;
204- await TriggerExecute ( events , context , linkedCts . Token ) . ConfigureAwait ( false ) ;
210+ await TriggerExecute ( events , context , _functionExecutionToken ) . ConfigureAwait ( false ) ;
205211 eventToCheckpoint = events . LastOrDefault ( ) ;
206212 }
207213
@@ -276,7 +282,7 @@ private async Task MonitorCachedEvents(DateTimeOffset? lastCheckpointTime, Cance
276282 var details = GetOperationDetails ( _mostRecentPartitionContext , "MaxWaitTimeElapsed" ) ;
277283 _logger . LogDebug ( $ "Partition Processor has waited MaxWaitTime since last invocation and is attempting to invoke function on all held events ({ details } )") ;
278284
279- await TriggerExecute ( triggerEvents , _mostRecentPartitionContext , backgroundCancellationTokenSource . Token ) . ConfigureAwait ( false ) ;
285+ await TriggerExecute ( triggerEvents , _mostRecentPartitionContext , _functionExecutionToken ) . ConfigureAwait ( false ) ;
280286 if ( ! backgroundCancellationTokenSource . Token . IsCancellationRequested )
281287 {
282288 await CheckpointAsync ( triggerEvents . Last ( ) , _mostRecentPartitionContext ) . ConfigureAwait ( false ) ;
@@ -408,7 +414,6 @@ protected virtual void Dispose(bool disposing)
408414 {
409415 if ( disposing )
410416 {
411- _cts . Dispose ( ) ;
412417 _cachedEventsBackgroundTaskCts ? . Dispose ( ) ;
413418 _cachedEventsGuard ? . Dispose ( ) ;
414419 }
0 commit comments