@@ -441,7 +441,6 @@ await PartTransferStatusEventHandler.RaiseAsync(
441441 {
442442 await TriggerCancellationAsync ( ) . ConfigureAwait ( false ) ;
443443 }
444- await CheckAndUpdateCancellationStateAsync ( ) . ConfigureAwait ( false ) ;
445444 }
446445 catch ( Exception cancellationException )
447446 {
@@ -460,6 +459,31 @@ await TransferFailedEventHandler.RaiseAsync(
460459 ClientDiagnostics )
461460 . ConfigureAwait ( false ) ;
462461 }
462+
463+ // Whether or not we were able to trigger the cancellation and successfully clean up
464+ // we should always call CheckAndUpdateCancellationStateAsync to correctly make
465+ // sure the job part is in the correct state.
466+ try
467+ {
468+ await CheckAndUpdateCancellationStateAsync ( ) . ConfigureAwait ( false ) ;
469+ }
470+ catch ( Exception checkUpdateException )
471+ {
472+ // If an exception is thrown while trying to clean up,
473+ // raise the failed event and prevent the transfer from hanging
474+ await TransferFailedEventHandler . RaiseAsync (
475+ new TransferItemFailedEventArgs (
476+ _dataTransfer . Id ,
477+ _sourceResource ,
478+ _destinationResource ,
479+ checkUpdateException ,
480+ false ,
481+ _cancellationToken ) ,
482+ nameof ( JobPartInternal ) ,
483+ nameof ( TransferFailedEventHandler ) ,
484+ ClientDiagnostics )
485+ . ConfigureAwait ( false ) ;
486+ }
463487 }
464488
465489 /// <summary>
@@ -472,12 +496,25 @@ public async virtual Task CleanupAbortedJobPartAsync()
472496 // If the failure occurred due to the file already existing or authentication,
473497 // and overwrite wasn't enabled, don't delete the existing file. We can remove
474498 // the unfinished file for other error types.
475- if ( JobPartFailureType . Other == _failureType )
499+ // If a Pause was called, we can remove the unfinished file.
500+ if ( JobPartFailureType . Other == _failureType || DataTransferState . Pausing == JobPartStatus . State )
476501 {
477502 // If the job part is paused or ended with failures
478503 // delete the destination resource because it could be unfinished or corrupted
479504 // If we resume we would have to start from the beginning anyways.
480- await _destinationResource . DeleteIfExistsAsync ( _cancellationToken ) . ConfigureAwait ( false ) ;
505+ try
506+ {
507+ // We can't pass the cancellation token
508+ // here due to the fact that the job's cancellation token has already been called.
509+ // We are cleaning up / deleting optimistically, which means that if
510+ // the clean / delete attempt fails, then we continue on. The failure may be due
511+ // to the overall reason why the job was cancelled in the first place.
512+ await _destinationResource . DeleteIfExistsAsync ( ) . ConfigureAwait ( false ) ;
513+ }
514+ catch
515+ {
516+ // We are cleaning up / deleting optimistically, move on if it fails.
517+ }
481518 }
482519 }
483520
0 commit comments