Problem with reference counting with the C library

Having problems with building or using CEF's C/C++ APIs? This forum is here to help. Please do not post bug reports or feature requests here.

Problem with reference counting with the C library

Postby bob » Thu Apr 28, 2022 2:19 pm

Hi,

I'm using the C library in an off-screen rendering context to:

1. Initialize cef
2. Wait for context to be initialized
3. Run message loop
4. Sleep 3s
5. Create a browser
6. Do something with the browser once it's created
7. Sleep 1s
8. Close the browser
9. Sleep 1s
10. Shutdown

However, on shutdown I get the following error:
Code: Select all
Check failed: global_request_context_->HasOneRef()


Based on this post, it seems it's related to the global_request_context not being released properly and more precisely a reference to a browser https://magpcss.org/ceforum/viewtopic.php?f=6&t=16450

However I'm closing the browser and the on_before_close life span callback is being hit. I never call the cef_browser_t->base.add_ref() method therefore, if I understood things correctly, I don't need to release anything. Or did I understand it wrong?

I took a look at the doc but I still have questions: https://bitbucket.org/chromiumembedded/cef/wiki/UsingTheCAPI.md

Decrement the reference count on a struct when receiving it as an argument from somewhere else after you're done using it:


Does that mean that in handlers methods like life_span_handler->on_after_created or render_handler->get_view_rect I should call cef_browser_t->base.release() explicitely?

Reverse any additional references that your code adds to a struct


If cef_browser_t->base.add_ref() is not called explicitely in the code at the moment of storing the browser somewhere, is it mandatory to call cef_browser_t->base.release() nonetheless? (this is purely theoretical so that I understand the logic behind).

Thanks in advance.
Cheers
Bob
bob
Newbie
 
Posts: 6
Joined: Thu Apr 21, 2022 12:26 pm

Re: Problem with reference counting with the C library

Postby magreenblatt » Thu Apr 28, 2022 3:49 pm

How are you running the message loop? Why are you using sleep?
magreenblatt
Site Admin
 
Posts: 12409
Joined: Fri May 29, 2009 6:57 pm

Re: Problem with reference counting with the C library

Postby bob » Fri Apr 29, 2022 2:11 am

I'm running the message loop using cef_run_message_loop and I'm using sleeps to simulate a normal workflow to debug shutdown crashes.

Basically here's the workflow:

[UI thread]: cef_initialize
[UI thread]: cef_run_message_loop
[Other thread]: sleep 3s
[Other thread]: cef_browser_host_create_browser
[Other thread]: OnAfterCreated -> store *cef_browser_t but without calling add_ref
[Other thread]: sleep 2s
[Other thread]: post task on UI thread to run browser->get_host->close_browser(1)
[Other thread]: OnBeforeClose -> reset pointer to *cef_browser_t but without calling release
[Other thread]: sleep 2s
[Other thread]: post task on UI thread to run cef_quit_message_loop
[UI thread]: once cef_run_message_loop is done blocking, cef_shutdown
bob
Newbie
 
Posts: 6
Joined: Thu Apr 21, 2022 12:26 pm

Re: Problem with reference counting with the C library

Postby magreenblatt » Fri Apr 29, 2022 10:52 am

With cef_run_message_loop the CEF callbacks will occur on the main/UI thread. Make sure that you are not sleeping on the main/UI thread.

Decrement the reference count on a struct when receiving it as an argument from somewhere else after you're done using it:
Does that mean that in handlers methods like life_span_handler->on_after_created or render_handler->get_view_rect I should call cef_browser_t->base.release() explicitely?

Yes.

Reverse any additional references that your code adds to a struct
If cef_browser_t->base.add_ref() is not called explicitely in the code at the moment of storing the browser somewhere, is it mandatory to call cef_browser_t->base.release() nonetheless? (this is purely theoretical so that I understand the logic behind).

If you keep a pointer to the cef_browser_t then you do not need to release the reference immediately in that particular callback. However, you do need to release the reference in any further callbacks that pass the cef_browser_t, and you do need to release the reference kept for your pointer at or before on_before_close.
magreenblatt
Site Admin
 
Posts: 12409
Joined: Fri May 29, 2009 6:57 pm

Re: Problem with reference counting with the C library

Postby bob » Wed May 11, 2022 5:23 am

First off, thanks for the clarifications.

However, after trying out many things, I'm still getting
Code: Select all
Check failed: global_request_context_->HasOneRef()


I've simplified the whole process (I've removed sleeps among other things) and here's what I'm doing ("..." means I've removed the code that seemed irrelevant to my question):

Code: Select all

cef_browser_t* _browser = NULL;

int main(int argc, char** argv) {
  ...

  cef_settings_t settings = {};
  ...
  settings.windowless_rendering_enabled = 1;

 ...

  cef_initialize(&main_args, &settings, app, NULL);

  int new_signals[] = {SIGINT,  SIGQUIT, SIGABRT, SIGTERM};
  int new_signals_len = (int)( sizeof(new_signals) / sizeof(new_signals[0]) );
  for (unsigned i = 0; i < new_signals_len; ++i) {
      signal(new_signals[i], &on_term);
  }

  cef_run_message_loop();

  cef_shutdown();
}

void on_term(int signal) {
    cef_browser_host_t *host = _browser->get_host(_browser);
    host->close_browser(host, 1);
}

void CEF_CALLBACK on_context_initialized(struct _cef_browser_process_handler_t* self) {
  cef_window_info_t window_info = {};
  window_info.windowless_rendering_enabled = 1;

  char url[] = "https://google.com";
  cef_string_t curl = {};
  cef_string_from_utf8(url, strlen(url), &curl);

  cef_browser_settings_t browser_settings = {};
  browser_settings.size = sizeof(cef_browser_settings_t);
  browser_settings.windowless_frame_rate = 30;

  ...

  cef_browser_host_create_browser(&window_info, client, &curl, &browser_settings, NULL, NULL);
}

void CEF_CALLBACK on_after_created(struct _cef_life_span_handler_t* self, struct _cef_browser_t* browser) {
    _browser = browser;
}

void CEF_CALLBACK on_before_close(struct _cef_life_span_handler_t* self, struct _cef_browser_t* browser) {
    browser->base.release((cef_base_ref_counted_t*)browser);

    _browser->base.release((cef_base_ref_counted_t*)_browser);
    _browser = NULL;

    cef_quit_message_loop();
}

void CEF_CALLBACK get_view_rect(struct _cef_render_handler_t* self, struct _cef_browser_t* browser, cef_rect_t* rect) {
    browser->base.release((cef_base_ref_counted_t*)browser);
}

void CEF_CALLBACK  on_paint(struct _cef_render_handler_t* self, struct _cef_browser_t* browser, cef_paint_element_type_t type, size_t dirtyRectsCount, cef_rect_t const* dirtyRects, const void* buffer, int width, int height) {
    browser->base.release((cef_base_ref_counted_t*)browser);
}


So basically it

- initializes cef with windowless rendering enabled
- runs message loop
- when context is initialized, it creates a browser with windowless rendering enabled
- when browser is created, it releases the pointer to cef_browser_t*
- on both render_handler callbacks, it releases the pointer to cef_browser_t*
- on SIGTERM, it closes the browser
- when browser is before close, callback cef_browser_t* is released, as well as the global cef_browser_t* _browser
- shuts down

And unfortunately I'm getting Check failed: global_request_context_->HasOneRef() as if a browser reference had not been released properly.

Is there anything I'm missing?
bob
Newbie
 
Posts: 6
Joined: Thu Apr 21, 2022 12:26 pm

Re: Problem with reference counting with the C library

Postby cybersight » Sun Feb 25, 2024 7:04 pm

I'm also getting the "Check failed: global_request_context_->HasOneRef()" error when running CEF in debug mode.

One thing I tried was to call CefBrowser->Release() until CefBrowser->HasOneRef() returns true inside the OnBeforeClose() callback, but that didn't help. Nor did it calling exactly once more to ensure that it hits 0. So I'm at a loss as to how to debug this.

Are there any other objects aside from CefBrowser that may be causing the DCHECK to fail?

The worst part is that CEF decides to take down the entire application if this condition isn't met, which is frustrating since it also includes the IDE I'm developing with. For fatal errors that would require restarting the browser process anyways, sure, but why for this?
Last edited by cybersight on Sun Feb 25, 2024 8:05 pm, edited 1 time in total.
cybersight
Techie
 
Posts: 38
Joined: Mon Dec 11, 2023 11:04 am

Re: Problem with reference counting with the C library

Postby cybersight » Sun Feb 25, 2024 7:34 pm

cybersight wrote:One thing I tried was to call CefBrowser->Release() until CefBrowser->HasOneRef() returns true inside the OnBeforeClose() callback, but that didn't help.


Actually, I do get different behavior when using this trick. CEF crashes immediately, rather than only after calling OnShutdown(), and the error message is:

Code: Select all
[0226/012713.591:FATAL:browser_platform_delegate.cc(35)] Check failed: !browser_.


Looking at the source, it's a DCHECK inside the deconstructor for CefBrowserPlatformDelegate:

Code: Select all
CefBrowserPlatformDelegate::~CefBrowserPlatformDelegate() {
  DCHECK(!browser_);
}


I don't know why it would be failing in this case and not the other, however. You'd think it'd be the other way around?
Last edited by cybersight on Sat Mar 02, 2024 9:03 pm, edited 1 time in total.
cybersight
Techie
 
Posts: 38
Joined: Mon Dec 11, 2023 11:04 am

Re: Problem with reference counting with the C library

Postby ndesktop » Mon Feb 26, 2024 3:06 am

Sleep will not help, most likely will block the loop and make the closing even more trickier. The correct way is to implement the close flow as in cefclient and only when the browser count drops to 0, then do the shutdown.
ndesktop
Master
 
Posts: 756
Joined: Thu Dec 03, 2015 10:10 am


Return to Support Forum

Who is online

Users browsing this forum: Google [Bot] and 191 guests