Skip to content

[Issue] UWB Issues on Unity App Shutdown #434

@RMichaelPickering

Description

@RMichaelPickering

What platform are you experiencing this issue on?

Windows x64

What version of UWB are you using?

2.2.8

What Unity version are you running?

6000.3.7f1

Describe what the issue you are experiencing is.

Summary

When calling Application.Quit() (or stopping play mode in the Unity Editor), two
bugs occur together:

  • Part ANullReferenceException in WebBrowserUIControls.OnDestroy() is
    reported as a Unity crash
  • Part B — The application hangs for several seconds (or indefinitely) before
    finally exiting, caused by WebBrowserClient.Dispose() blocking inside
    communicationsManager.Shutdown()

Part A — NullReferenceException in WebBrowserUIControls.OnDestroy

File: Runtime/WebBrowserUIControls.cs, line 46

private void OnDestroy()
{
    webBrowserUi.browserClient.OnUrlChanged -= OnUrlChanged;  // ← throws NRE
}

webBrowserUi or webBrowserUi.browserClient is null or refers to a destroyed
Unity native object at the time OnDestroy() is called. Unity does not guarantee
the order in which OnDestroy() is called across components, even on the same
GameObject. If WebBrowserUIBasic has already been torn down before
WebBrowserUIControls.OnDestroy() runs, the reference is invalid.

Error logged:

NullReferenceException: Object reference not set to an instance of an object
  at VoltstroStudios.UnityWebBrowser.WebBrowserUIControls.OnDestroy ()

Unity reports this as a crash and attempts to upload a crash report.

Proposed fix (WebBrowserUIControls.cs line 44–47):

private void OnDestroy()
{
    if (webBrowserUi != null && webBrowserUi.browserClient != null)
        webBrowserUi.browserClient.OnUrlChanged -= OnUrlChanged;
}

Part B — Application.Quit hangs in WebBrowserClient.Dispose

File: Runtime/Core/WebBrowserClient.cs, around line 1190

WebBrowserClient.Dispose() calls communicationsManager.Shutdown() before
killing the engine process. communicationsManager.Shutdown() calls
engineProxy.Shutdown() which is a blocking RPC call that waits for the CEF
browser engine process to respond and exit cleanly. CEF shutdown can be slow (it
spawns multiple child processes), causing the Unity application to hang for several
seconds or indefinitely on exit.

The engineProcess.KillProcess() call that would resolve the hang is placed
after the blocking call and is therefore never reached in time:

// Current order in Dispose() — causes hang:
communicationsManager.Shutdown();   // ← blocks here waiting for CEF
communicationsManager?.Dispose();
if (!engineProcess.HasExited)
    engineProcess.KillProcess();    // ← never reached promptly

Proposed fix — kill the engine process first, then attempt graceful shutdown
(which will now fail fast on a broken pipe, caught by the existing try/catch):

// Fixed order — kill first, then cleanup:
if (engineProcess != null && !engineProcess.HasExited)
    engineProcess.KillProcess();    // terminate CEF immediately

try
{
    if (ReadySignalReceived && IsConnected)
        communicationsManager.Shutdown();   // fails fast, exception caught below
}
catch (Exception ex)
{
    logger.Error($"Some error occured while shutting down the engine! {ex}");
}

communicationsManager?.Dispose();

if (engineProcess != null)
{
    engineProcess.Dispose();
    engineProcess = null;
}

This matches the behaviour of WindowProcess.KillProcess() which already uses
TerminateJobObject — a hard, immediate termination — so the intent is clearly
to force-terminate when disposing.


Provide reproducible steps for this issue.

  1. Add WebBrowserUIBasic and WebBrowserUIControls to a scene
  2. Call Application.Quit() (or stop play mode in the Editor)
  3. Part A: Observe NullReferenceException crash report in Player.log
  4. Part B: Observe application hangs for several seconds before exiting

Any additional info you like to provide?

Temporary Workaround

Part A — no active workaround.
The NRE still occurs in WebBrowserUIControls.OnDestroy() during Unity's normal
teardown sweep and appears in Player.log as Failed to upload Crash Report to Cloud Diagnostics. (the upload fails fast because Unity services are already
partially torn down at that point). This is cosmetic — it does not cause a hang
or affect functionality. An earlier attempt to suppress it via DestroyImmediate
in OnApplicationQuit() was abandoned: it fired the NRE while the network stack
was still fully up, causing the crash report to upload successfully and block exit
for several seconds. The upstream null-guard fix (above) is the correct resolution.

Part B — working workaround: EmbeddedBrowserController.ForceShutdown()
Called from MonoBehaviour.OnApplicationQuit() (fires before any OnDestroy()),
this uses reflection to reach WebBrowserClient.engineProcess.KillProcess() and
terminate the CEF browser process immediately. With the process already dead,
communicationsManager.Shutdown() hits a broken pipe and fails fast (exception
caught by UWB's own try/catch inside Dispose()), so teardown completes in
milliseconds. The [UWB]: Some error occured while shutting down the engine! log
lines are expected and benign.

For now, the workaround for Part B is an acceptable option, though slightly messy due to additional errors to log!

Metadata

Metadata

Assignees

No one assigned

    Labels

    issueSomething is broken

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions