Safe File Termination (Ctrl+C Support)
Relevant source files
The following files were used as context for generating this wiki page:
Safe File Termination ensures that media recordings remain playable even if the user interrupts the process. The system intercepts termination signals to trigger a graceful shutdown of capture streams and a proper finalization of the AVAssetWriter session.
Without this mechanism, abruptly killing the process prevents the file header from being written, resulting in corrupted, unreadable video or audio files. This feature is a core safety requirement for the project's CLI tools.
Sources: README.md:14, Sources/Spike/RecSystemCmd.swift:51
Signal Interception Architecture
The system uses a custom signal handler to manage external interruptions. Instead of allowing the operating system to terminate the process immediately, the tool captures SIGINT (Ctrl+C) and SIGTERM (standard termination request).
Signal Handler Mechanism
The function installStopSignalHandler performs the following steps:
- Sets
SIGINTandSIGTERMtoSIG_IGNto prevent default termination. - Creates a
DispatchSourceSignalfor both signals on a dedicated queue. - Executes a provided closure when a signal arrives.
Sources: Sources/Spike/Util.swift:80-92
This diagram shows how the custom handler prevents immediate process death and redirects the event to the application logic.
Sources: Sources/Spike/Util.swift:82-90, Sources/Spike/RecSystemCmd.swift:54
Recording Session Lifecycle
The recording loop in runRecordingSession manages the transition from active capture to finalization. It uses a DispatchSemaphore to block the main thread until the specified duration expires or a stop signal is received.
Termination Flow
When the signal handler triggers the semaphore, the session proceeds through these steps:
- Capture Stop: Stops
SckCaptureandMicCapturestreams to prevent new data from arriving. - Asset Marking: Calls
markAsFinished()on allAVAssetWriterInputinstances. - Finalization: Calls
AVAssetWriter.finishWriting()to commit the media data and write the file footer.
Sources: Sources/Spike/RecSystemCmd.swift:53-61, Sources/Spike/MovieWriter.swift:147-150
Logic Components
| Component | Function | File Reference |
|---|---|---|
installStopSignalHandler | Attaches custom logic to SIGINT/SIGTERM | Util.swift |
DispatchSemaphore | Blocks execution until duration or signal | RecSystemCmd.swift |
SckCapture.stop() | Discontinues ScreenCaptureKit stream | SckCapture.swift |
MovieWriter.finish() | Safely closes AVAssetWriter session | MovieWriter.swift |
Data Integrity and Finalization
The MovieWriter class ensures that the session state is valid before attempting to finish the file. If the session never started (e.g., no frames were captured), it cancels the writing process to avoid creating invalid files.
func finish() async throws {
lock.lock()
let started = sessionStarted
lock.unlock()
guard started else {
writer.cancelWriting()
return
}
videoInput?.markAsFinished()
for a in audioInputs.values { a.markAsFinished() }
await writer.finishWriting()
if writer.status != .completed {
throw SpikeError("AVAssetWriter 失敗: \(String(describing: writer.error))")
}
}
Sources: Sources/Spike/MovieWriter.swift:142-156
Integration and Verification
The project includes integration tests to verify that SIGINT produces valid, playable files. The test script starts a recording, waits a few seconds, sends a kill -INT signal, and then inspects the resulting file's duration and validity.
The sequence diagram illustrates the automated validation of the safe termination sequence.
Sources: scripts/integration-test.sh:183-195
Test Criteria
- Exit Code: The process must exit with code
0. - Duration: The recorded file duration must be greater than 0 (e.g., between 2.5s and 8.0s for a 4s test).
- Playability: The file must be readable by
AVURLAssetfor inspection.
Sources: scripts/integration-test.sh:190-194, Sources/Spike/InspectCmd.swift:33-40
Summary
Safe File Termination is implemented by overriding standard Unix signal behaviors to trigger a coordinated shutdown of the AVFoundation and ScreenCaptureKit pipelines. By using semaphores to bridge asynchronous signal handling with synchronous session control, the system guarantees that recordings are finalized and metadata is written correctly even under manual interruption.