Reputation: 10009
I have a simple CEF application for OSX that is based on the provided cefsample app. One notable change is that I'm running in single-process mode (settings.single_process = true
). I'm getting a crash on exit due to an assertion failure. Oddly, this crash occurs in release builds, too, not just debug builds.
Can anyone (CEF gurus) help me understand what the problem is here? The stacktrace says that I'm calling an "IO-only" function from a thread that disallows IO. The only IO I'm doing is that I'm loading static html file from the app bundle. That seems like it shouldn't require special handling. But, OK, let's assume that it does require me to "adjust the call to base::ThreadRestrictions::SetIOAllowed() in this thread's startup" as recommended in the assertion error -- I have no idea how or where to do that.
Here is the stacktrace:
[1120/110258:FATAL:thread_restrictions.cc(38)] Function marked as IO-only was called from a thread that disallows IO! If this thread really should be allowed to make IO calls, adjust the call to base::ThreadRestrictions::SetIOAllowed() in this thread's startup.
0 Chromium Embedded Framework 0x0068b8cf base::debug::StackTrace::StackTrace() + 63
1 Chromium Embedded Framework 0x0068b92b base::debug::StackTrace::StackTrace() + 43
2 Chromium Embedded Framework 0x00719d52 logging::LogMessage::~LogMessage() + 82
3 Chromium Embedded Framework 0x00718a8b logging::LogMessage::~LogMessage() + 43
4 Chromium Embedded Framework 0x00832984 base::ThreadRestrictions::AssertIOAllowed() + 276
5 Chromium Embedded Framework 0x00810100 base::PlatformThread::Join(base::PlatformThreadHandle) + 48
6 Chromium Embedded Framework 0x008263a3 base::Thread::Stop() + 131
7 Chromium Embedded Framework 0x08413d8f content::InProcessRendererThread::~InProcessRendererThread() + 63
8 Chromium Embedded Framework 0x08413e0b content::InProcessRendererThread::~InProcessRendererThread() + 43
9 Chromium Embedded Framework 0x08413e5e content::InProcessRendererThread::~InProcessRendererThread() + 46
10 Chromium Embedded Framework 0x07bab824 base::DefaultDeleter<base::Thread>::operator()(base::Thread*) const + 68
11 Chromium Embedded Framework 0x07bab7aa base::internal::scoped_ptr_impl<base::Thread, base::DefaultDeleter<base::Thread> >::reset(base::Thread*) + 122
12 Chromium Embedded Framework 0x07b93bc9 scoped_ptr<base::Thread, base::DefaultDeleter<base::Thread> >::reset(base::Thread*) + 57
13 Chromium Embedded Framework 0x07b85dc8 content::RenderProcessHostImpl::~RenderProcessHostImpl() + 392
14 Chromium Embedded Framework 0x07b8632b content::RenderProcessHostImpl::~RenderProcessHostImpl() + 43
15 Chromium Embedded Framework 0x07b864be content::RenderProcessHostImpl::~RenderProcessHostImpl() + 46
16 Chromium Embedded Framework 0x00534d88 CefContentRendererClient::RunSingleProcessCleanupOnUIThread() + 872
17 Chromium Embedded Framework 0x00534888 CefContentRendererClient::RunSingleProcessCleanup() + 344
18 Chromium Embedded Framework 0x003d6ab5 CefContext::FinalizeShutdown() + 69
19 Chromium Embedded Framework 0x003d5b26 CefContext::Shutdown() + 694
20 Chromium Embedded Framework 0x003d5750 CefShutdown() + 512
21 Chromium Embedded Framework 0x0027dbe7 cef_shutdown + 39
22 CEFSimpleSample 0x000c1a37 CefShutdown() + 39
23 CEFSimpleSample 0x000baefc main + 388
24 libdyld.dylib 0x96e33701 start + 1
25 ??? 0x00000003 0x0 + 3
And here is part of my app. The assertion is failing in the CEFShutdown() method, with is a framework method:
// Entry point function for the browser process.
int main(int argc, char* argv[]) {
// Provide CEF with command-line arguments.
CefMainArgs main_args(argc, argv);
// ClientHandler implements application-level callbacks. It will create the first
// browser instance in OnContextInitialized() after CEF has initialized.
CefRefPtr<ClientHandler> app(new ClientHandler);
// Initialize the AutoRelease pool.
NSAutoreleasePool* autopool = [[NSAutoreleasePool alloc] init];
// Initialize the SimpleApplication instance.
[SimpleApplication sharedApplication];
// Specify CEF global settings here.
CefSettings settings;
settings.single_process = true;
// Initialize CEF for the browser process.
CefInitialize(main_args, settings, app.get(), NULL);
// Create the application delegate.
NSObject* delegate = [[SimpleAppDelegate alloc] init];
[delegate performSelectorOnMainThread:@selector(createApplication:)
withObject:nil
waitUntilDone:NO];
// Run the CEF message loop. This will block until CefQuitMessageLoop() is
// called.
CefRunMessageLoop();
// Shut down CEF.
CefShutdown();
// Release the delegate.
[delegate release];
// Release the AutoRelease pool.
[autopool release];
return 0;
}
Upvotes: 1
Views: 1269
Reputation: 6347
This is a known bug of CEF:
https://code.google.com/p/chromiumembedded/issues/detail?id=1182
Chromium typically distinguishes the UI thread from the IO threads, and the message is an assertion that these were mixed up. It is not related to your own IO.
You need to look at the stack to understand what's going on. Functions with a namespace typically belong to Chromium while functions in global namespace are part of CEF itself. And CEF-related crashes often are due to the glue to embed Chromium...
The guilty function probably is method RunSingleProcessCleanupOnUIThread
of CefContentRendererClient. The name implies it is meant to run on UI thread.
void CefContentRendererClient::RunSingleProcessCleanupOnUIThread() {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
// Clean up the single existing RenderProcessHost.
content::RenderProcessHost* host = NULL;
content::RenderProcessHost::iterator iterator(
content::RenderProcessHost::AllHostsIterator());
if (!iterator.IsAtEnd()) {
host = iterator.GetCurrentValue();
host->Cleanup();
iterator.Advance();
DCHECK(iterator.IsAtEnd());
}
DCHECK(host);
// Clear the run_renderer_in_process() flag to avoid a DCHECK in the
// RenderProcessHost destructor.
content::RenderProcessHost::SetRunRendererInProcess(false);
// Deletion of the RenderProcessHost object will stop the render thread and
// result in a call to WillDestroyCurrentMessageLoop.
// Cleanup() will cause deletion to be posted as a task on the UI thread but
// this task will only execute when running in multi-threaded message loop
// mode (because otherwise the UI message loop has already stopped). Therefore
// we need to explicitly delete the object when not running in this mode.
if (!CefContext::Get()->settings().multi_threaded_message_loop)
delete host;
}
The crash occurs at the very botton of the function. Indeed multi_threaded_message_loop
is false (multi_threaded_message_loop
is for Windows only), and we can follow the crash in the stack trace to RenderProcessHostImpl destructor.
It should be clear from the code and the comments that this is quite a dirty hack around Chromium, trying to reproduce something Chromium does elsewhere. I actually believe the DCHECK (Chromium assert macro) workaround is outdated as g_run_renderer_in_process_
which is set to false here is not checked in the destructor.
The following comment in BrowserMainLoop::ShutdownThreadsAndCleanUp()
might provide a clue:
// Teardown may start in PostMainMessageLoopRun, and during teardown we
// need to be able to perform IO.
base::ThreadRestrictions::SetIOAllowed(true);
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(base::IgnoreResult(&base::ThreadRestrictions::SetIOAllowed),
true));
A fix could be to invoke base::ThreadRestrictions::SetIOAllowed(true);
before invoking the destructor in RunSingleProcessCleanupOnUIThread()
method. Unfortunately, you will need to recompile CEF for this purpose, and you cannot call this expression in your main function (before calling CefShutdown()
) as CEF does not expose these Chromium internals.
Upvotes: 2