Repository navigation
fix: stop exceptions from callbacks unwinding through WebRTC - #335
Merged
Merged
Conversation
devopvoid
added this pull request to stack #336
October 9, 2026 22:10
The callbacks WebRTC invokes ended with ExceptionCheck(), which turns a Java exception thrown by the application into a C++ exception. It then unwound through WebRTC's frames, which are built without unwinding support, leaving WebRTC's locks held or ending the process, depending on the platform. The same held for C++ exceptions thrown while converting a callback's arguments. Every such callback now catches its own C++ exceptions, turns them into Java ones, and ends with ReportPendingException(), which clears the pending exception and hands it to the thread's uncaught exception handler: the peer connection, session description, data channel, DTLS, DTMF and stats observers, the audio and video track sinks, the track observer, the audio device's recorded-data sink and the desktop capture callback. The add-ICE-candidate and data channel send observers, which already reported exceptions this way, now share the helper. While in these callbacks: - the audio sinks hold their byte array in a JavaLocalRef, so it is freed when the callback throws, and pass jint values to the Java callback instead of size_t through varargs; - the track observer no longer leaks a local reference per event; - CustomVideoSource.pushFrame catches conversion errors instead of letting a C++ exception leave the JNI call.
devopvoid
force-pushed
the
fix/observer-exceptions
branch
from
October 9, 2026 22:50
b969638 to
0687b83
Compare
SendableMetatype
added a commit
to EduGeyser/webrtc-java
that referenced
this pull request
Oct 10, 2026
Brings in the work upstream added on October 10: the missing peer connection signaling and control calls (devopvoid#334), callback exceptions kept from unwinding through WebRTC (devopvoid#335), first-packet observers, sender and receiver IDs and track volume (devopvoid#338), video processors for camera and desktop frames (devopvoid#342), and fixes to audio device changes, camera hotplug and media stream views (devopvoid#333, devopvoid#337, devopvoid#341, devopvoid#343). Fork-side adaptations: - setAudioPlayout and setAudioRecording reach the voice engine, so the data channels variant leaves them out, like the other audio natives. - The new tests that need audio or video carry the media tag. - The Maven publish action stays deleted.
SendableMetatype
added a commit
to EduGeyser/webrtc-java
that referenced
this pull request
Oct 10, 2026
Brings in the work upstream added on October 10: the missing peer connection signaling and control calls (devopvoid#334), callback exceptions kept from unwinding through WebRTC (devopvoid#335), first-packet observers, sender and receiver IDs and track volume (devopvoid#338), video processors for camera and desktop frames (devopvoid#342), and fixes to audio device changes, camera hotplug and media stream views (devopvoid#333, devopvoid#337, devopvoid#341, devopvoid#343). Fork-side adaptations: - setBitrate, setAudioPlayout and setAudioRecording reach the call and the voice engine, which a factory without a media engine does not create, so the data channels variant leaves them out, like the other media natives. - The new tests that need audio or video carry the media tag. - The Maven publish action stays deleted.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Builds on #334 (now merged), which moved the
ReportUncaughtExceptionhelper toWebRTCUtils.Problem
The callbacks WebRTC invokes ended with
ExceptionCheck(env), which turns a pending Java exception into a C++ exception. When the application's observer threw, or converting a callback's arguments failed, that exception unwound through WebRTC's frames. WebRTC is built without unwinding support, so this leaves its locks held, which shows up later as a silent hang (the same failure as the earlier LogSink deadlock), or ends the process, depending on the platform.Fix
Every callback that WebRTC invokes now catches its own C++ exceptions and turns them into Java ones (
ThrowCxxJavaException), then ends with the newReportPendingException(env), which clears the pending exception and hands it to the calling thread's uncaught exception handler, as the JVM does for an exception escaping a Java thread. Converted:PeerConnectionObserver(all callbacks),CreateSessionDescriptionObserver,SetSessionDescriptionObserverRTCDataChannelObserver,RTCDtlsTransportObserver,RTCDtmfSenderObserver,RTCStatsCollectorCallbackAudioTrackSink,VideoTrackSink,MediaStreamTrackObserverAudioTransportSink(the audio device's recorded-data sink),DesktopCaptureCallbackThe observers from #334 and the data channel send observer, which already reported exceptions this way, now share the helper instead of carrying their own copy.
Converters that throw are unchanged: they run inside JNI calls or callbacks that now catch.
Also fixed in these callbacks
AudioTrackSinkandAudioTransportSinkhold their byte array in aJavaLocalRef, so it is freed when the callback throws (on a native thread a leaked local reference is never reclaimed), and passjintvalues to the Java callback instead ofsize_tthrough varargs.MediaStreamTrackObserverreleased the Java track's local reference on every mute or end event instead of deleting it.CustomVideoSource.pushFrameandpushFrameTimestampedlet a C++ exception from converting the frame leave the JNI call; they now throw it as a Java exception.Tests
ObserverExceptionTestsmakes the stats callback, the create-offer observer, the peer connection observer (onIceCandidate,onConnectionChange) and a data channel observer throw on every call, and checks that each exception reaches the uncaught exception handler, that WebRTC keeps working (the next stats and offer arrive, the peers connect, every message is delivered), and thatclose()returns within 10 seconds.The full
webrtcsuite passes on Windows (253 run, 0 failures, 2 skipped), also under-Pjni-checkwith no JNI errors.