Can you drive CEF with DoMessageLoopWork without polling

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.

Can you drive CEF with DoMessageLoopWork without polling

Postby Osspial » Fri Jan 31, 2020 9:42 pm

Hi! I'm working on a codebase that's using CEF to manage the UI, and am trying to drive the CEF message loop with cef_do_message_loop_work. I've set external_message_pump to true in cef_settings_t, and am calling cef_do_message_loop_work whenever on_schedule_message_pump_work requests it. I am not calling cef_do_message_loop_work intermittently on a timer - it is only getting called when CEF schedules it. I am also using windowless rendering.

However, I've been unable to get this working reliably - sometimes, I'm able to load http://www.google.com and initially navigate it, but upon resizing the window or attempting to watch a video rendering stops completely. Sometimes, the webpage doesn't even start rendering when the application initially launches. Sometimes, everything just works perfectly. The different behaviors show up at different frequencies with different machine frequencies, but but buggy behavior has been reproduced on both Windows and Linux with the same codebase. On Windows, I've noticed that enabling heap verification with Application Verifier makes buggy behavior happen more frequently.

Calling cef_do_message_loop_work intermittently fixes those issues, but doing that when work isn't available wastes user CPU time and isn't ideal. Is it possible to externally drive a CEF event loop without without resorting to calling cef_do_message_loop_work on a timer? If so, how might I be misusing CEF in a way that would cause that?

Source code exhibiting this issue is available here: https://github.com/anlumo/cef. The application is written in Rust and is using an API we're developing that wraps around CEF's C API and exposes it more conveniently in Rust. Both the "embedded" example and "embedded_wgpu" example exhibit issues, although they're more prominent in "embedded_wgpu". The examples can be run with "cd examples & cargo r --example (embedded|embedded_wgpu) --release".
Osspial
Newbie
 
Posts: 7
Joined: Fri Jan 31, 2020 6:02 pm

Re: Can you drive CEF with DoMessageLoopWork without polling

Postby magreenblatt » Fri Jan 31, 2020 11:03 pm

You can compare to the cefclient implementation of --external-message-pump.
magreenblatt
Site Admin
 
Posts: 12409
Joined: Fri May 29, 2009 6:57 pm

Re: Can you drive CEF with DoMessageLoopWork without polling

Postby Czarek » Fri Jan 31, 2020 11:37 pm

External message pump is problematic on Linux since always. I use it only on Mac and similarly to yours case I have to use both external message pump and CefDoMessageLoopWork at the same time, otherwise things don't work with wxWidgets framework. With Qt on the other hand I use only message pump and strangely CefDoMessageLoopWork doesn't work with Qt at all. It used to work, but things change with Chromium updates. Not tested with latest versions.
Maintainer of the CEF Python, PHP Desktop and CEF C API projects. My LinkedIn.
User avatar
Czarek
Virtuoso
 
Posts: 1927
Joined: Sun Nov 06, 2011 2:12 am

Re: Can you drive CEF with DoMessageLoopWork without polling

Postby Osspial » Mon Feb 03, 2020 1:58 pm

magreenblatt wrote:You can compare to the cefclient implementation of --external-message-pump.


I've compared it with that, and cefclient seems to call cef_do_message_loop_work on a timer. I'll fully admit that there's something I could be missing there, though - I don't have much C++ experience, so grokking the structure of the C++ code and building an accurate mental model for how that codebase works has been a challenge.
Osspial
Newbie
 
Posts: 7
Joined: Fri Jan 31, 2020 6:02 pm

Re: Can you drive CEF with DoMessageLoopWork without polling

Postby magreenblatt » Mon Feb 03, 2020 2:22 pm

A timer is used to execute the CefDoMessageLoopWork calls at the delay requested by OnScheduleMessagePumpWork, or at kMaxTimerDelay, whichever occurs first. See comments in MainMessageLoopExternalPump.
magreenblatt
Site Admin
 
Posts: 12409
Joined: Fri May 29, 2009 6:57 pm

Re: Can you drive CEF with DoMessageLoopWork without polling

Postby Osspial » Mon Feb 03, 2020 2:59 pm

magreenblatt wrote:A timer is used to execute the CefDoMessageLoopWork calls at the delay requested by OnScheduleMessagePumpWork, or at kMaxTimerDelay, whichever occurs first. See comments in MainMessageLoopExternalPump.


Yeah, I realize that. Is there any way for my application to only call CefDoMessageLoopWork when CEF has work that needs to be done, instead of actively polling CEF? It seems bizarre that OnScheduleMessagePumpWork doesn't schedule another call to CefDoMessageLoopWork when there's more work to do, especially since CefDoMessageLoopWork's documentation outright states that OnScheduleMessagePumpWork is the mechanism you should use to schedule additional work, and OnScheduleMessagePumpWork can explicitly be called from any thread (presumably so that worker threads can notify the main thread when there's more work to be done):

Code: Select all
// Perform a single iteration of CEF message loop processing. This function is
// provided for cases where the CEF message loop must be integrated into an
// existing application message loop. Use of this function is not recommended
// for most users; use either the cef_run_message_loop() function or
// CefSettings.multi_threaded_message_loop if possible. When using this function
// care must be taken to balance performance against excessive CPU usage. It is
// recommended to enable the CefSettings.external_message_pump option when using
// this function so that
// cef_browser_process_handler_t::on_schedule_message_pump_work() callbacks can
// facilitate the scheduling process. This function should only be called on the
// main application thread and only if cef_initialize() is called with a
// CefSettings.multi_threaded_message_loop value of false (0). This function
// will not block.
///
CEF_EXPORT void cef_do_message_loop_work();

///
// ***Called from any thread*** when work has been scheduled for the browser process
// main (UI) thread. This callback is used in combination with CefSettings.
// external_message_pump and cef_do_message_loop_work() in cases where the CEF
// message loop must be integrated into an existing application message loop
// (see additional comments and warnings on CefDoMessageLoopWork). This
// callback should schedule a cef_do_message_loop_work() call to happen on the
// main (UI) thread. |delay_ms| is the requested delay in milliseconds. If
// |delay_ms| is <= 0 then the call should happen reasonably soon. If
// |delay_ms| is > 0 then the call should be scheduled to happen after the
// specified delay and any currently pending scheduled call should be
// cancelled.
///
void(CEF_CALLBACK* on_schedule_message_pump_work)


Should cef_do_message_loop_work's documentation be updated to reflect that you need to call it on a timer, even if no work has been scheduled? Alternately (and preferably), how difficult would it be to make on_schedule_message_pump_work proactively schedule work so that it can drive CEF without a timer?
Osspial
Newbie
 
Posts: 7
Joined: Fri Jan 31, 2020 6:02 pm

Re: Can you drive CEF with DoMessageLoopWork without polling

Postby magreenblatt » Mon Feb 03, 2020 3:16 pm

Where do you see polling in the cefclient implementation of MainMessageLoopExternalPump?

OnScheduleMessagePumpWork tells you to call CefDoMessageLoopWork after the specified delay. How do you propose to implement this delay without using a timer?
magreenblatt
Site Admin
 
Posts: 12409
Joined: Fri May 29, 2009 6:57 pm

Re: Can you drive CEF with DoMessageLoopWork without polling

Postby Osspial » Mon Feb 03, 2020 3:26 pm

Sorry for being unclear there.

magreenblatt wrote:Where do you see polling in the cefclient implementation of MainMessageLoopExternalPump?

kMaxTimerDelay defaults to 1/30th of a second, resulting in cefclient asking CEF if work needs to be done at 30hz even if no work has been actively scheduled. That's polling by definition right there, even if it's not spinning around the event loop at the maximum speed the CPU allows.

magreenblatt wrote:OnScheduleMessagePumpWork tells you to call CefDoMessageLoopWork after the specified delay. How do you propose to implement this delay without using a timer?

I think using a timer there to schedule delayed work is good, but I don't see why it's necessary or appropriate for the timer to call CefDoMessageLoopWork at 30hz even when the user isn't interacting with the program and they have a static webpage open.
Osspial
Newbie
 
Posts: 7
Joined: Fri Jan 31, 2020 6:02 pm

Re: Can you drive CEF with DoMessageLoopWork without polling

Postby magreenblatt » Mon Feb 03, 2020 3:39 pm

Osspial wrote:I think using a timer there to schedule delayed work is good, but I don't see why it's necessary or appropriate for the timer to call CefDoMessageLoopWork at 30hz even when the user isn't interacting with the program and they have a static webpage open.

OnScheduleMessagePumpWork can only forecast events that Chromium already knows about, such as currently in-flight tasks and timers. You don't necessarily need to call CefDoMessageLoopWork at 30hz. However, if you don't call CefDoMessageLoopWork "often enough" then Chromium has no time slice to determine if new work needs to be performed. If you're confident that no new interaction is happening with Chromium in your application (including any type of input event, resize, repaint, network request, etc) then you don't need to call CefDoMessageLoopWork except when requested by OnScheduleMessagePumpWork.
magreenblatt
Site Admin
 
Posts: 12409
Joined: Fri May 29, 2009 6:57 pm

Re: Can you drive CEF with DoMessageLoopWork without polling

Postby cybersight » Sat Mar 02, 2024 10:19 pm

magreenblatt wrote:If you're confident that no new interaction is happening with Chromium in your application (including any type of input event, resize, repaint, network request, etc) then you don't need to call CefDoMessageLoopWork except when requested by OnScheduleMessagePumpWork.


If you're using OSR, is it feasible to only call CefDoMessageLoopWork after sending an input or resize event to a CefBrowser, or are there other types of events that need to be processed by the event loop that aren't initiated by the host application? Excluding any that are already handled by OnScheduleMessagePumpWork.
cybersight
Techie
 
Posts: 38
Joined: Mon Dec 11, 2023 11:04 am

Next

Return to Support Forum

Who is online

Users browsing this forum: No registered users and 202 guests