Skip to content

Commit 190fdc9

Browse files
committed
fix(recording): stop the WGC helper from hanging on stop with no audio/webcam/cursor
The final "wait for stop" in wgc-capture's main() shared the same mutex/cv as the video-writer thread's per-frame encode loop. That loop holds the mutex for the full duration of each frame's GPU copy + software encode call, so if the encode pipeline stalls (slow software encoder, flaky GPU driver), the main thread can't even acquire the lock to check whether `stop` was requested -- turning a stall into a silent, unbounded hang with no [stop-timing] diagnostic output at all (issue #115). This reproduces specifically when audio/mic/webcam/cursor are all disabled because in that configuration nothing else is left holding/releasing that mutex on a faster cadence to give the stop check a chance to run. Give CaptureControl a dedicated stopMutex/stopCv used only to signal "stop was requested", decoupled from the frame-processing mutex. The stdin reader thread now notifies both cvs on stop, and the final stop-wait uses only the dedicated pair, so stop detection never depends on the frame pump releasing its lock. Fixes #115
1 parent d5966ed commit 190fdc9

1 file changed

Lines changed: 25 additions & 2 deletions

File tree

electron/native/wgc-capture/src/main.cpp

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,19 @@ struct CaptureControl {
5757
std::atomic<bool> paused = false;
5858
std::mutex mutex;
5959
std::condition_variable cv;
60+
// Dedicated mutex/cv pair used ONLY to signal "stop was requested" to the
61+
// main thread. The frame-processing pipeline (writeVideoFrames) holds
62+
// `mutex` for the full duration of each frame's GPU copy + encode call,
63+
// which can stall for a long time on slow software encoders or flaky GPU
64+
// drivers (e.g. hybrid-graphics laptops). If the final "wait for stop"
65+
// below shared that same mutex, it would have to wait for the encode
66+
// pipeline to release the lock before it could even check whether stop
67+
// was requested — turning an encode stall into a silent, unbounded stop
68+
// hang with no diagnostic output (see issue #115). Keeping stop signaling
69+
// on its own uncontended mutex means the stop request is always observed
70+
// promptly, regardless of what the frame pump is doing.
71+
std::mutex stopMutex;
72+
std::condition_variable stopCv;
6073
std::chrono::steady_clock::time_point pauseStartedAt;
6174
std::chrono::steady_clock::duration totalPausedDuration{};
6275

@@ -359,6 +372,7 @@ void readCaptureCommands(CaptureControl& control, const std::function<void(bool)
359372
if (line == "stop" || line == "q" || line == "quit") {
360373
control.stopRequested = true;
361374
control.cv.notify_all();
375+
control.stopCv.notify_all();
362376
return;
363377
}
364378
if (line == "pause") {
@@ -378,6 +392,7 @@ void readCaptureCommands(CaptureControl& control, const std::function<void(bool)
378392
}
379393
control.stopRequested = true;
380394
control.cv.notify_all();
395+
control.stopCv.notify_all();
381396
}
382397

383398
} // namespace
@@ -586,6 +601,7 @@ int main(int argc, char* argv[]) {
586601
encodeFailed = true;
587602
control.stopRequested = true;
588603
control.cv.notify_all();
604+
control.stopCv.notify_all();
589605
return;
590606
}
591607
}
@@ -657,6 +673,7 @@ int main(int argc, char* argv[]) {
657673
encodeFailed = true;
658674
control.stopRequested = true;
659675
control.cv.notify_all();
676+
control.stopCv.notify_all();
660677
return;
661678
}
662679
lastWrittenWebcamSequence = latestWebcamSequence;
@@ -669,6 +686,7 @@ int main(int argc, char* argv[]) {
669686
encodeFailed = true;
670687
control.stopRequested = true;
671688
control.cv.notify_all();
689+
control.stopCv.notify_all();
672690
return;
673691
}
674692
if (latestFrameTexture) {
@@ -711,6 +729,7 @@ int main(int argc, char* argv[]) {
711729
encodeFailed = true;
712730
control.stopRequested = true;
713731
control.cv.notify_all();
732+
control.stopCv.notify_all();
714733
return false;
715734
}
716735
return true;
@@ -840,8 +859,12 @@ int main(int argc, char* argv[]) {
840859
std::cout << "Recording started" << std::endl;
841860

842861
{
843-
std::unique_lock lock(mutex);
844-
control.cv.wait(lock, [&] {
862+
// Wait on the dedicated stop mutex/cv (not the frame-processing
863+
// `mutex`), so this check is never gated on the video writer thread
864+
// releasing a lock it may be holding for a long time inside a slow
865+
// or stalled encode call. See the CaptureControl::stopMutex comment.
866+
std::unique_lock lock(control.stopMutex);
867+
control.stopCv.wait(lock, [&] {
845868
return control.stopRequested.load();
846869
});
847870
}

0 commit comments

Comments
 (0)