Crash closing browser

Having problems with building or using the CefGlue .NET/Mono binding? Ask your questions here.

Moderator: fddima

Crash closing browser

Postby joaoneves » Tue Jun 30, 2020 10:00 am

I'm getting a random crash when closing a browser instance. The stack is as follows:

Code: Select all
    libcef.dll![thunk]:CefCToCppRefCounted<class CefAppCToCpp,class CefApp,struct _cef_app_t>::Release`vtordisp{4294967292,0}' (void)   C++   Non-user code. Symbols loaded.
>   libcef.dll!CefBrowserHostImpl::~CefBrowserHostImpl() Line 0   C++   Symbols loaded.
    libcef.dll![thunk]:CefBrowserHostImpl::`vector deleting destructor'`adjustor{32}' (unsigned int)   C++   Non-user code. Symbols loaded.
    libcef.dll![thunk]:CefBrowserHostImpl::Release`vtordisp{4294967292,0}' (void)   C++   Non-user code. Symbols loaded.
    libcef.dll!CefCppToCRefCounted<CefBrowserCppToC,CefBrowser,_cef_browser_t>::Release() Line 84   C++   Symbols loaded.
    libcef.dll!CefCppToCRefCounted<CefAuthCallbackCppToC,CefAuthCallback,_cef_auth_callback_t>::struct_release(_cef_base_ref_counted_t * base) Line 165   C++   Symbols loaded.
    [Managed to Native Transition]      Annotated Frame
    Xilium.CefGlue.dll!Xilium.CefGlue.Interop.cef_browser_t.release(Xilium.CefGlue.Interop.cef_browser_t* self) Line 212   C#   Symbols loaded.
    Xilium.CefGlue.dll!Xilium.CefGlue.CefBrowser.Release() Line 102   C#   Symbols loaded.
    Xilium.CefGlue.dll!Xilium.CefGlue.CefBrowser.~CefBrowser() Line 60   C#   Symbols loaded.


I'm releasing the browser instance only once (double checked), but sometimes, not always I get this crash. Since this is on the native side, I'm clueless on what to do to prevent this.
Any help or pointers are appreciated.

Using cef 75.
joaoneves
Techie
 
Posts: 12
Joined: Fri Nov 29, 2019 5:45 am

Re: Crash closing browser

Postby Czarek » Tue Jun 30, 2020 12:34 pm

What is the full stack trace?
Maintainer of the CEF Python, PHP Desktop and CEF C API projects. I'm available for contract work, see my resume.
User avatar
Czarek
Virtuoso
 
Posts: 1874
Joined: Sun Nov 06, 2011 2:12 am

Re: Crash closing browser

Postby joaoneves » Wed Jul 01, 2020 3:27 am

Thats is the full stack. This is called from the .net finalizer (thread).

This happens frequently when closing the browser right after loading a page.
joaoneves
Techie
 
Posts: 12
Joined: Fri Nov 29, 2019 5:45 am

Re: Crash closing browser

Postby Czarek » Wed Jul 01, 2020 3:51 am

At what point in app life time are you closing the browser and what is the procedure you follow?
Maintainer of the CEF Python, PHP Desktop and CEF C API projects. I'm available for contract work, see my resume.
User avatar
Czarek
Virtuoso
 
Posts: 1874
Joined: Sun Nov 06, 2011 2:12 am

Re: Crash closing browser

Postby joaoneves » Wed Jul 01, 2020 4:09 am

This happens sometimes is real life scenarios but to reproduce it more frequently I created the following testing scenario:

Code: Select all
Loop 30 times:
   Create browser instance
   Browser on after created is called:
      Wait 1 sec on (app) UI thread - optional (happens with or without this delay)
      Close browser (force = true)
      Browser on closed is called:
         Dispose browser


After the 4th or 5th iteration the crash happens. I reduced the testing conditions to this and I don't even have to load a page.
joaoneves
Techie
 
Posts: 12
Joined: Fri Nov 29, 2019 5:45 am

Re: Crash closing browser

Postby ndesktop » Wed Jul 01, 2020 5:06 am

You need a shutdown loop, I suppose.
CloseBrowser is not synchronous (someone correct me if I'm wrong), it's the OnBeforeClose callback that tells you the browser is *about to* close.
So this is how I did it:
- DoClose. If browser window was reparented to a tab, main window etc. reparent browser window to something dummy, or remove the child window style (GWL_STYLE: & ~WS_CHILD, | WS_POPUP)
- OnBeforeClose: notify parent object (in my example, I'm calling OnBrowserClosed(browser))
- OnBrowserClosed is implemented in main window (main form in C#) which does specific cleanup, then check if can close or not.
Here are the implementation excerpts.
Client handler:
Code: Select all
LONG ClientHandler::BrowsersAddRef()
{
    CEF_REQUIRE_UI_THREAD();
    return ++m_BrowsersCount;
}
LONG ClientHandler::BrowsersRelease()
{
    CEF_REQUIRE_UI_THREAD();
    return --m_BrowsersCount;
}
bool ClientHandler::HaveBrowsers() const
{
    CEF_REQUIRE_UI_THREAD();
    return m_BrowsersCount != 0;
}
LONG ClientHandler::BrowsersCount() const
{
    CEF_REQUIRE_UI_THREAD();
    return m_BrowsersCount;
}
Note. Call BrowsersAddRef() in OnAfterCreated, BrowsersRelease() in OnBeforeClose.

bool ClientHandler::DoClose(CefRefPtr<CefBrowser> browser)
{
    CEF_REQUIRE_UI_THREAD();
    if(browser.get() != 0)
    {
        HWND hwndHost = browser->GetHost()->GetWindowHandle();
        ::SetParent(hwndHost, NULL);
        DWORD dwHostStyle = (DWORD)::GetWindowLongPtrW(hwndHost, GWL_STYLE);
        if(dwHostStyle & WS_CHILD)
        {
            dwHostStyle &= ~WS_CHILD;
            dwHostStyle |= WS_POPUP;
        }
        ::SetWindowLongPtrW(hwndHost, GWL_STYLE, (LONG_PTR)dwHostStyle);
    }
    return false;
}

void ClientHandler::OnBeforeClose(CefRefPtr<CefBrowser> browser)
{
... cleanup
   BrowsersRelease();
  _NotifyBrowserClosed(browser);
}
void ClientHandler::_NotifyBrowserClosed(CefRefPtr<CefBrowser> browser)
{
    if(!CefCurrentlyOn(TID_UI)) {
        // Execute this method on the main thread.
        CefCreateClosureTask(base::Bind(&ClientHandler::_NotifyBrowserClosed, this, browser));
        return;
    }
    m_pParent->OnBrowserClosed(browser);
}
Note: m_pParent is the main application window.


Main app window:
Code: Select all
void MainWindow::OnBrowserClosed(CefRefPtr<CefBrowser> browser)
{
    CEF_REQUIRE_UI_THREAD();
...
    OnBrowserWindowDestroyed(browser);
    return;
}
void MainWindow::OnBrowserWindowDestroyed(CefRefPtr<CefBrowser> browser)
{
... specific cleanup
    browsers_destroyed_ = !_handler->HaveBrowsers(); // browser count is NOT > 0 ?
    if(browsers_destroyed_) { // yes, we have 0 browsers
        if (!window_destroyed_) { // main window not destroyed
            if(_handler->isClosing()) { // isClosing returns true if we entered the shutdown phase, for example click the Close button on main window
                CloseIfNeeded(true); // force close main window
            }
        }
    }
    NotifyDestroyedIfDone();
}
Note. browsers_destroyed_ and window_destroyed_ are bools, first indicates "all browsers are destroyed, browser count is 0" and the second means "main window is destroyed".

void MainWindow::CloseIfNeeded(bool force)
{
    if (GetSafeHwnd()) {
        if (force) {
            ::DestroyWindow(GetSafeHwnd());
        }
        else {
            ::PostMessage(GetSafeHwnd(), WM_CLOSE, 0, 0);
        }
    }
}
void MainWindow::OnDestroyed()
{
    window_destroyed_ = true;
    NotifyDestroyedIfDone();
}
Note. OnDestroyed is called from WM_NCDESTROY.

void MainWindow::NotifyDestroyedIfDone()
{
    // (re)check if browsers destroyed
    browsers_destroyed_ = !_handler->HaveBrowsers();
    if(window_destroyed_ && browsers_destroyed_) {
        OnWindowDestroyed();
    }
}
void MainWindow::OnWindowDestroyed()
{
    Application::QuitMessageLoop();
}

void Application::QuitMessageLoop()
{
  CefDoMessageLoopWork();
  CefQuitMessageLoop();
}


The basic idea is this (also consider that not all things might be helpful for you, I'm describing my particular case):
- when WM_CLOSE is first sent, we don't close (return TRUE from WM_CLOSE), mark the client handler as closing (ClientHandler::isClosing will return true from now on), mark main window as closing (we enter in a shutdown phase), then post UWM_CLOSE to the main window (UWM_CLOSE is an user-defined message)
- UWM_CLOSE arrives; do the cleanup (hide main window, dispose resources etc.) and arm an one time shutdown timer
- WM_TIMER: if timer ID == shutdown timer ID, check the ClientHandler::BrowserCount(); if 0, destroy main window and call Application::QuitMessageLoop(), which consists of two calls, a last CefDoMessageLoopWork() + CefQuitMessageLoop()
- if browser count is not 0, rearm the one time shutdown timer and next timer will recheck if browser count dropped to 0.
How you pick how much time or how many times you rearm the shutdown timer is up to you.
ndesktop
Expert
 
Posts: 422
Joined: Thu Dec 03, 2015 10:10 am

Re: Crash closing browser

Postby joaoneves » Wed Jul 01, 2020 3:20 pm

I think I found a Ref count bug on cefglue that was causing the crash.
joaoneves
Techie
 
Posts: 12
Joined: Fri Nov 29, 2019 5:45 am


Return to CefGlue Forum

Who is online

Users browsing this forum: Google [Bot] and 1 guest