diff --git a/electron/native/wgc-capture/src/main.cpp b/electron/native/wgc-capture/src/main.cpp index 30a3069d6..7c05153a4 100644 --- a/electron/native/wgc-capture/src/main.cpp +++ b/electron/native/wgc-capture/src/main.cpp @@ -57,6 +57,19 @@ struct CaptureControl { std::atomic paused = false; std::mutex mutex; std::condition_variable cv; + // Dedicated mutex/cv pair used ONLY to signal "stop was requested" to the + // main thread. The frame-processing pipeline (writeVideoFrames) holds + // `mutex` for the full duration of each frame's GPU copy + encode call, + // which can stall for a long time on slow software encoders or flaky GPU + // drivers (e.g. hybrid-graphics laptops). If the final "wait for stop" + // below shared that same mutex, it would have to wait for the encode + // pipeline to release the lock before it could even check whether stop + // was requested — turning an encode stall into a silent, unbounded stop + // hang with no diagnostic output (see issue #115). Keeping stop signaling + // on its own uncontended mutex means the stop request is always observed + // promptly, regardless of what the frame pump is doing. + std::mutex stopMutex; + std::condition_variable stopCv; std::chrono::steady_clock::time_point pauseStartedAt; std::chrono::steady_clock::duration totalPausedDuration{}; @@ -359,6 +372,7 @@ void readCaptureCommands(CaptureControl& control, const std::function