@@ -90,6 +90,8 @@ public sealed partial class LocalObjectData : IGrainContext, IGrainCallCancellat
9090 private static readonly Func < object ? , Task > HandleFunc = self => ( ( LocalObjectData ) self ! ) . LocalObjectMessagePumpAsync ( ) ;
9191 private readonly InvokableObjectManager _manager ;
9292 private readonly Dictionary < Message , Task ? > _runningRequests = [ ] ;
93+ private readonly Dictionary < Message , int > _cancellationLeaseCounts = [ ] ;
94+ private readonly HashSet < Message > _completedRequests = [ ] ;
9395 private Task ? _messagePumpTask ;
9496
9597 internal LocalObjectData ( IAddressable obj , ObserverGrainId observerId , InvokableObjectManager manager )
@@ -324,12 +326,23 @@ private async Task ProcessMessageAsync(Message message)
324326 }
325327 finally
326328 {
327- message . DisposeOwnedBody ( ) ;
328-
329- // Clear the running request when done.
329+ var disposeMessage = false ;
330330 lock ( Messages )
331331 {
332332 _runningRequests . Remove ( message ) ;
333+ if ( _cancellationLeaseCounts . ContainsKey ( message ) )
334+ {
335+ _completedRequests . Add ( message ) ;
336+ }
337+ else
338+ {
339+ disposeMessage = true ;
340+ }
341+ }
342+
343+ if ( disposeMessage )
344+ {
345+ message . DisposeOwnedBody ( ) ;
333346 }
334347 }
335348 }
@@ -437,6 +450,7 @@ bool TryCancelRequest()
437450 {
438451 Message ? message = null ;
439452 var wasWaiting = false ;
453+ var hasCancellationLease = false ;
440454 lock ( Messages )
441455 {
442456 // Check the running requests.
@@ -445,6 +459,9 @@ bool TryCancelRequest()
445459 if ( runningRequest . Id == messageId && runningRequest . SendingGrain == senderGrainId )
446460 {
447461 message = runningRequest ;
462+ _cancellationLeaseCounts . TryGetValue ( message , out var leaseCount ) ;
463+ _cancellationLeaseCounts [ message ] = leaseCount + 1 ;
464+ hasCancellationLease = true ;
448465 break ;
449466 }
450467 }
@@ -478,34 +495,67 @@ bool TryCancelRequest()
478495 }
479496
480497 var didCancel = false ;
481- if ( message is not null )
498+ try
482499 {
483- // The message never began executing, so send a canceled response immediately.
484- // If the message did begin executing, wait for it to observe the cancellation token and respond itself.
485- if ( wasWaiting )
500+ if ( message is not null )
486501 {
487- try
502+ // The message never began executing, so send a canceled response immediately.
503+ // If the message did begin executing, wait for it to observe the cancellation token and respond itself.
504+ if ( wasWaiting )
488505 {
489- _manager . runtimeClient . SendResponse ( message , Response . FromException ( new OperationCanceledException ( ) ) ) ;
490- didCancel = true ;
506+ try
507+ {
508+ _manager . runtimeClient . SendResponse ( message , Response . FromException ( new OperationCanceledException ( ) ) ) ;
509+ didCancel = true ;
510+ }
511+ finally
512+ {
513+ message . DisposeOwnedBody ( ) ;
514+ }
515+ }
516+ else if ( message . BodyObject is IInvokable invokableRequest )
517+ {
518+ didCancel = TryCancelInvokable ( invokableRequest ) || ! invokableRequest . IsCancellable ;
491519 }
492- finally
520+ else
493521 {
494- message . DisposeOwnedBody ( ) ;
522+ // Assume the request is not cancellable.
523+ didCancel = true ;
495524 }
496525 }
497- else if ( message . BodyObject is IInvokable invokableRequest )
526+ }
527+ finally
528+ {
529+ if ( hasCancellationLease )
498530 {
499- didCancel = TryCancelInvokable ( invokableRequest ) || ! invokableRequest . IsCancellable ;
531+ ReleaseCancellationLease ( message ! ) ;
532+ }
533+ }
534+
535+ return didCancel ;
536+ }
537+
538+ void ReleaseCancellationLease ( Message message )
539+ {
540+ var disposeMessage = false ;
541+ lock ( Messages )
542+ {
543+ var leaseCount = _cancellationLeaseCounts [ message ] ;
544+ if ( leaseCount == 1 )
545+ {
546+ _cancellationLeaseCounts . Remove ( message ) ;
547+ disposeMessage = _completedRequests . Remove ( message ) ;
500548 }
501549 else
502550 {
503- // Assume the request is not cancellable.
504- didCancel = true ;
551+ _cancellationLeaseCounts [ message ] = leaseCount - 1 ;
505552 }
506553 }
507554
508- return didCancel ;
555+ if ( disposeMessage )
556+ {
557+ message . DisposeOwnedBody ( ) ;
558+ }
509559 }
510560
511561 bool TryCancelInvokable ( IInvokable request )
0 commit comments