@@ -488,6 +488,7 @@ final class ModelManager {
488488 size: ModelSize ,
489489 onProgress: @escaping ( Double ) -> Void
490490 ) async throws {
491+ try Task . checkCancellation ( )
491492 guard let spec = SherpaModelCatalog . spec ( for: size) else {
492493 throw ModelManagerError . modelNotAvailable ( size. rawValue)
493494 }
@@ -548,6 +549,11 @@ final class ModelManager {
548549 )
549550 }
550551
552+ // Cancellation may arrive during hashing/extraction. Stop before
553+ // changing the installed model; once the swap begins, finish its
554+ // rollback-safe transaction without interruption.
555+ try Task . checkCancellation ( )
556+
551557 // Swap the finished model in without a window where neither copy
552558 // is in place: move any existing install aside first, and only
553559 // delete it once the new one has landed.
@@ -588,6 +594,8 @@ final class ModelManager {
588594
589595 onProgress ( 1.0 )
590596 VocaLogger . info ( . modelManager, " ONNX model ' \( size. rawValue) ' installed at: \( destination. path) " )
597+ } catch is CancellationError {
598+ throw CancellationError ( )
591599 } catch {
592600 VocaLogger . error ( . modelManager, " Download failed for ' \( size. rawValue) ': \( error. localizedDescription) " )
593601 throw ModelManagerError . downloadFailed ( reason: error. localizedDescription)
@@ -597,23 +605,27 @@ final class ModelManager {
597605 /// SHA-256 of a file, read in chunks so a large archive never has to be
598606 /// held in memory all at once.
599607 static func sha256Hex( ofFileAt url: URL ) throws -> String {
608+ try Task . checkCancellation ( )
600609 let handle = try FileHandle ( forReadingFrom: url)
601610 defer { try ? handle. close ( ) }
602611
603612 var hasher = SHA256 ( )
604613 while let chunk = try handle. read ( upToCount: 1 << 20 ) , !chunk. isEmpty {
614+ try Task . checkCancellation ( )
605615 hasher. update ( data: chunk)
606616 }
607617 return hasher. finalize ( ) . map { String ( format: " %02x " , $0) } . joined ( )
608618 }
609619
610620 /// Extract a .tar.bz2 archive using the system tar.
611621 static func extractTarArchive( at archive: URL , into directory: URL ) throws {
622+ try Task . checkCancellation ( )
612623 let process = Process ( )
613624 process. executableURL = URL ( fileURLWithPath: " /usr/bin/tar " )
614625 process. arguments = [ " xjf " , archive. path, " -C " , directory. path]
615626 try process. run ( )
616627 process. waitUntilExit ( )
628+ try Task . checkCancellation ( )
617629 guard process. terminationStatus == 0 else {
618630 throw ModelManagerError . downloadFailed (
619631 reason: " Archive extraction failed (tar exited with \( process. terminationStatus) ) "
0 commit comments