integrating custom MessageLoop with BrowserMainLoop

1,463 views
Skip to first unread message

Marshall Greenblatt

unread,
Jan 4, 2012, 5:40:50 PM1/4/12
to John Abd-El-Malek, chromium-dev
Hi John,

The Chromium Embedded Framework (CEF) currently supports a run mode where the UI thread is integrated with an existing application message loop. This is useful for applications using frameworks like MFC where the application message loop is buried in the framework layer. In the current single-process architecture this mode works as follows:

1. CEF extends MessageLoopForUI with an implementation that calls Quit() from DoIdleWork() [1].

2. The client application calls Run() in a timely manner to execute one iteration of the MessageLoop.

An overly simplified client implementation might look like this:

int main(void) {
  CefInitialize(); // create the other (non UI) threads and the UI MessageLoop

  while (run_message_loop) {
    SomeCustomWork();
    CefDoMessageLoopWork(); // call Run() on the UI MessageLoop
  }

  CefShutdown(); // destroy the other (non UI) threads and the UI MessageLoop

  return 0;
}

In order to support this approach with the content API and BrowserMainLoop I think the following changes are needed:

1. Add a BrowserMainParts callback for providing my own MessageLoop implementation that would then be assigned as main_message_loop_ in BrowserMainLoop::MainMessageLoopStart().

2. Change ContentMain, BrowserMain and BrowserMainLoop::RunMainMessageLoopParts() so that the call can return without cleanup/shutdown being executed.

3. Expose a separate function that executes all of the cleanup/shutdown logic.

The main difficultly that I foresee is the refactoring required for #2 and #3 since we use scoped variables for a good chunk of this functionality. Is there some easier approach that I might be missing?

Thanks,
Marshall

[1] http://code.google.com/p/chromiumembedded/source/browse/trunk/libcef/cef_process.cc

Adam Simmons

unread,
Jan 5, 2012, 6:03:37 PM1/5/12
to Chromium-dev
Hi guys,

Lead developer of Awesomium here. We had a similar problem-- I
mentioned this to jam last week, I've copied the message below:

> The other hurdle we've faced concerns the "ContentMain" architecture-- we cannot expect users of our library to give us control over their main loop. We've worked around this by doing a couple things:
>
> 1. Wrap all of the initialization/destruction code within ContentMain and BrowserMain into a singleton that users can create/destroy arbitrarily during their application's execution.
> 2. Define our own BrowserMainLoop implementation so we can create threads without entering the main loop.
> 2. Create a method that allows users to pump the UI message loop for a brief timeslice during their main update loop.
> 3. Create a separate child-process executable that calls ContentMain and use that for hosting all sub-processes.

This works but it's a bit of a pain to maintain. We'd rather like to
see the content API move away from the "BrowserMain" concept entirely
so that it can be used in more than just "Browser-Like" applications.
We'll be happy to contribute back code or changes if anyone is
interested.

-- Adam
> [1]http://code.google.com/p/chromiumembedded/source/browse/trunk/libcef/...

John Abd-El-Malek

unread,
Jan 5, 2012, 6:20:28 PM1/5/12
to ad...@khrona.com, Marshall Greenblatt, Chromium-dev
Hey guys, first of all, glad to see you using the content API. If we can have embedding frameworks and embedders of chrome using the content layer, it will simplify things :)

It seems that there are two distinct use cases: people who want to build a browser using content, and people who want to embed it in their existing application. The current approach of BrowserMain makes it trivial to do the former, but hard to do the latter as you guys point out. I would prefer we find a solution that makes it possible to do the second case with little or no work, but still keeps the first case easy. 

What if we expose an interface around BrowserMain that has two methods: PreRunMessageLoop and PostRunMessageLoop. The existing calling code in browser_main_loop.cc would switch to only calling these methods, instead of the various ones it calls now (the above functions can still call these internally). Embedding frameworks like CEF/Awesomium can then use the BrowserMain interface to set things up and tear things down. They would create their own MessageLoop object and pump it whenever they want.

Thoughts?


--
Chromium Developers mailing list: chromi...@chromium.org
View archives, change email options, or unsubscribe:
   http://groups.google.com/a/chromium.org/group/chromium-dev

Marshall Greenblatt

unread,
Jan 6, 2012, 1:21:57 PM1/6/12
to John Abd-El-Malek, ad...@khrona.com, Chromium-dev
On Thu, Jan 5, 2012 at 6:20 PM, John Abd-El-Malek <j...@chromium.org> wrote:
Hey guys, first of all, glad to see you using the content API. If we can have embedding frameworks and embedders of chrome using the content layer, it will simplify things :)

It seems that there are two distinct use cases: people who want to build a browser using content, and people who want to embed it in their existing application. The current approach of BrowserMain makes it trivial to do the former, but hard to do the latter as you guys point out. I would prefer we find a solution that makes it possible to do the second case with little or no work, but still keeps the first case easy. 

I agree.
 

What if we expose an interface around BrowserMain that has two methods: PreRunMessageLoop and PostRunMessageLoop. The existing calling code in browser_main_loop.cc would switch to only calling these methods, instead of the various ones it calls now (the above functions can still call these internally). Embedding frameworks like CEF/Awesomium can then use the BrowserMain interface to set things up and tear things down. They would create their own MessageLoop object and pump it whenever they want.

Thoughts?

Sounds reasonable to me. Will it be possible to initialize/shutdown BrowserMain multiple times in the same process?

Having embedders selectively duplicate parts from ContentMain is probably fine since some of the things done by ContentMain should only be done once (initializing AtExitManager, CommandLine, etc) and others seem like they could conflict with the host application (initializing COM, registering signal handlers, etc).
 

John Abd-El-Malek

unread,
Jan 6, 2012, 1:40:50 PM1/6/12
to Marshall Greenblatt, ad...@khrona.com, Chromium-dev
On Fri, Jan 6, 2012 at 10:21 AM, Marshall Greenblatt <magree...@gmail.com> wrote:
On Thu, Jan 5, 2012 at 6:20 PM, John Abd-El-Malek <j...@chromium.org> wrote:
Hey guys, first of all, glad to see you using the content API. If we can have embedding frameworks and embedders of chrome using the content layer, it will simplify things :)

It seems that there are two distinct use cases: people who want to build a browser using content, and people who want to embed it in their existing application. The current approach of BrowserMain makes it trivial to do the former, but hard to do the latter as you guys point out. I would prefer we find a solution that makes it possible to do the second case with little or no work, but still keeps the first case easy. 

I agree.
 

What if we expose an interface around BrowserMain that has two methods: PreRunMessageLoop and PostRunMessageLoop. The existing calling code in browser_main_loop.cc would switch to only calling these methods, instead of the various ones it calls now (the above functions can still call these internally). Embedding frameworks like CEF/Awesomium can then use the BrowserMain interface to set things up and tear things down. They would create their own MessageLoop object and pump it whenever they want.

Thoughts?

Sounds reasonable to me. Will it be possible to initialize/shutdown BrowserMain multiple times in the same process?

In theory this would be great. In practice, I'm not sure how many singeltons etc exist that might keep pointers to deleted objects etc. Only way to be sure would be to try it out :)

Jonathan Dixon

unread,
Jan 6, 2012, 1:46:20 PM1/6/12
to magree...@gmail.com, John Abd-El-Malek, chromium-dev
Sorry if this is off topic for the main topic of your mail, but hopefully useful to throw this in here...


On 4 January 2012 22:40, Marshall Greenblatt <magree...@gmail.com> wrote:
Hi John,

The Chromium Embedded Framework (CEF) currently supports a run mode where the UI thread is integrated with an existing application message loop. This is useful for applications using frameworks like MFC where the application message loop is buried in the framework layer. In the current single-process architecture this mode works as follows:

1. CEF extends MessageLoopForUI with an implementation that calls Quit() from DoIdleWork() [1].


You may also want to override ScheduleWork and ScheduleDelayedWork to move this from polling to an event driven design:
This will allow your "SomeCustomWork" method below to know how long it may execute for, and when it should yield or otherwise and arrange to call CefDoMessageLoopWork().
e.g. if you're living in another framework's event loop, such as MFC, ScheduleDelayedWork would just post a timer in that framework, which on firing would result in the call to CefDoMessageLoopWork().

 
2. The client application calls Run() in a timely manner to execute one iteration of the MessageLoop.

An overly simplified client implementation might look like this:

int main(void) {
  CefInitialize(); // create the other (non UI) threads and the UI MessageLoop

  while (run_message_loop) {
    SomeCustomWork();
    CefDoMessageLoopWork(); // call Run() on the UI MessageLoop
  }

  CefShutdown(); // destroy the other (non UI) threads and the UI MessageLoop

  return 0;
}

In order to support this approach with the content API and BrowserMainLoop I think the following changes are needed:

1. Add a BrowserMainParts callback for providing my own MessageLoop implementation that would then be assigned as main_message_loop_ in BrowserMainLoop::MainMessageLoopStart().

2. Change ContentMain, BrowserMain and BrowserMainLoop::RunMainMessageLoopParts() so that the call can return without cleanup/shutdown being executed.

3. Expose a separate function that executes all of the cleanup/shutdown logic.

The main difficultly that I foresee is the refactoring required for #2 and #3 since we use scoped variables for a good chunk of this functionality. Is there some easier approach that I might be missing?

Thanks,
Marshall

[1] http://code.google.com/p/chromiumembedded/source/browse/trunk/libcef/cef_process.cc

--

Adam Simmons

unread,
Jan 6, 2012, 2:57:23 PM1/6/12
to Chromium-dev
Exposing an interface around BrowserMain and allowing embedders to use
their own MessageLoop sounds like a good compromise to me. We're fine
with duplicating some of the initialization in ContentMain.
Regarding multiple series of instances, we've tried it and there are
indeed some issues with singletons (starting with AtExitManager) that
will need to be addressed. Maybe something to be experimented with
after these changes are in.
On Jan 6, 12:40 pm, John Abd-El-Malek <j...@chromium.org> wrote:
> On Fri, Jan 6, 2012 at 10:21 AM, Marshall Greenblatt <magreenbl...@gmail.com
> >>> Chromium Developers mailing list: chromium-...@chromium.org

John Abd-El-Malek

unread,
Jan 6, 2012, 3:36:28 PM1/6/12
to ad...@khrona.com, Marshall Greenblatt, Chromium-dev
Sounds good.

By the way it would be nice as well if we can avoid duplication ContentMain. Duplicating it would non-deal for the same reasons as BrowserMain. So maybe we can similarly split up ContaintMain into three methods (prerun ML, run ML, and post run ML). Perhaps we can also pass in an option flags (i.e. dont init COM, signal handlers etc).

Chromium Developers mailing list: chromi...@chromium.org

Marshall Greenblatt

unread,
Jan 6, 2012, 3:53:25 PM1/6/12
to jo...@chromium.org, John Abd-El-Malek, chromium-dev
On Fri, Jan 6, 2012 at 1:46 PM, Jonathan Dixon <jo...@chromium.org> wrote:
Sorry if this is off topic for the main topic of your mail, but hopefully useful to throw this in here...


On 4 January 2012 22:40, Marshall Greenblatt <magree...@gmail.com> wrote:
Hi John,

The Chromium Embedded Framework (CEF) currently supports a run mode where the UI thread is integrated with an existing application message loop. This is useful for applications using frameworks like MFC where the application message loop is buried in the framework layer. In the current single-process architecture this mode works as follows:

1. CEF extends MessageLoopForUI with an implementation that calls Quit() from DoIdleWork() [1].


You may also want to override ScheduleWork and ScheduleDelayedWork to move this from polling to an event driven design:
This will allow your "SomeCustomWork" method below to know how long it may execute for, and when it should yield or otherwise and arrange to call CefDoMessageLoopWork().
e.g. if you're living in another framework's event loop, such as MFC, ScheduleDelayedWork would just post a timer in that framework, which on firing would result in the call to CefDoMessageLoopWork().

Good idea. I'll have to try that and see how it affects performance and CPU usage for things like animation and video playback.
 

Marshall Greenblatt

unread,
Jan 6, 2012, 4:07:19 PM1/6/12
to John Abd-El-Malek, ad...@khrona.com, Chromium-dev
On Fri, Jan 6, 2012 at 3:36 PM, John Abd-El-Malek <j...@chromium.org> wrote:
Sounds good.

By the way it would be nice as well if we can avoid duplication ContentMain. Duplicating it would non-deal for the same reasons as BrowserMain. So maybe we can similarly split up ContaintMain into three methods (prerun ML, run ML, and post run ML). Perhaps we can also pass in an option flags (i.e. dont init COM, signal handlers etc).

Yes, less code duplication would be good. I think the code path for the other (non-browser) processes can be the same for all content API users, so the single ContentMain function could remain in place for launching those processes.  For CEF I'm planning to create a stub executable that is used for launching all but the browser process. Both the stub executable and the client application link to the same dynamic library that contains the content API.
 

Marshall Greenblatt

unread,
Jan 11, 2012, 4:06:12 PM1/11/12
to John Abd-El-Malek, ad...@khrona.com, Chromium-dev
On Fri, Jan 6, 2012 at 4:07 PM, Marshall Greenblatt <magree...@gmail.com> wrote:
On Fri, Jan 6, 2012 at 3:36 PM, John Abd-El-Malek <j...@chromium.org> wrote:
Sounds good.

By the way it would be nice as well if we can avoid duplication ContentMain. Duplicating it would non-deal for the same reasons as BrowserMain. So maybe we can similarly split up ContaintMain into three methods (prerun ML, run ML, and post run ML). Perhaps we can also pass in an option flags (i.e. dont init COM, signal handlers etc).

Yes, less code duplication would be good. I think the code path for the other (non-browser) processes can be the same for all content API users, so the single ContentMain function could remain in place for launching those processes.  For CEF I'm planning to create a stub executable that is used for launching all but the browser process. Both the stub executable and the client application link to the same dynamic library that contains the content API.

I've taken a first stab at the implementation here: http://codereview.chromium.org/9190018/
 
Reply all
Reply to author
Forward
0 new messages