Fix a crash when calling CefShutdown() before destroying all browser windows (issue #159).

git-svn-id: https://chromiumembedded.googlecode.com/svn/trunk@157 5089003a-bbd8-11dd-ad1f-f1f9622dbc98
This commit is contained in:
Marshall Greenblatt 2011-01-07 01:06:10 +00:00
parent 650cb1d41d
commit c7959e2106
5 changed files with 126 additions and 27 deletions

View File

@ -483,8 +483,11 @@ bool CefBrowser::CreateBrowser(CefWindowInfo& windowInfo, bool popup,
CefRefPtr<CefHandler> handler, CefRefPtr<CefHandler> handler,
const CefString& url) const CefString& url)
{ {
if(!_Context.get()) // Verify that the context is in a valid state.
if (!CONTEXT_STATE_VALID()) {
NOTREACHED();
return false; return false;
}
CefString newUrl = url; CefString newUrl = url;
CefBrowserSettings settings(_Context->browser_defaults()); CefBrowserSettings settings(_Context->browser_defaults());
@ -510,8 +513,17 @@ bool CefBrowser::CreateBrowser(CefWindowInfo& windowInfo, bool popup,
CefRefPtr<CefBrowser> CefBrowser::CreateBrowserSync(CefWindowInfo& windowInfo, CefRefPtr<CefBrowser> CefBrowser::CreateBrowserSync(CefWindowInfo& windowInfo,
bool popup, CefRefPtr<CefHandler> handler, const CefString& url) bool popup, CefRefPtr<CefHandler> handler, const CefString& url)
{ {
if(!_Context.get() || !CefThread::CurrentlyOn(CefThread::UI)) // Verify that the context is in a valid state.
if (!CONTEXT_STATE_VALID()) {
NOTREACHED();
return NULL; return NULL;
}
// Verify that this method is being called on the UI thread.
if (!CefThread::CurrentlyOn(CefThread::UI)) {
NOTREACHED();
return NULL;
}
CefString newUrl = url; CefString newUrl = url;
CefRefPtr<CefBrowser> alternateBrowser; CefRefPtr<CefBrowser> alternateBrowser;

View File

@ -21,7 +21,7 @@ CefRefPtr<CefContext> _Context;
bool CefInitialize(const CefSettings& settings, bool CefInitialize(const CefSettings& settings,
const CefBrowserSettings& browser_defaults) const CefBrowserSettings& browser_defaults)
{ {
// Return true if the context is already initialized // Return true if the global context already exists.
if(_Context.get()) if(_Context.get())
return true; return true;
@ -31,33 +31,48 @@ bool CefInitialize(const CefSettings& settings,
return false; return false;
} }
// Create the new global context object // Create the new global context object.
_Context = new CefContext(); _Context = new CefContext();
// Initialize the global context // Initialize the global context.
return _Context->Initialize(settings, browser_defaults); return _Context->Initialize(settings, browser_defaults);
} }
void CefShutdown() void CefShutdown()
{ {
// Verify that the context is already initialized // Verify that the context is in a valid state.
if(!_Context.get()) if (!CONTEXT_STATE_VALID()) {
NOTREACHED();
return; return;
}
// Shut down the global context // Must always be called on the same thread as Initialize.
if(!_Context->process()->CalledOnValidThread()) {
NOTREACHED();
return;
}
// Shut down the global context. This will block until shutdown is complete.
_Context->Shutdown(); _Context->Shutdown();
// Delete the global context object
// Delete the global context object.
_Context = NULL; _Context = NULL;
} }
void CefDoMessageLoopWork() void CefDoMessageLoopWork()
{ {
// Verify that the context is already initialized. // Verify that the context is in a valid state.
if(!_Context.get() || !_Context->process()) if (!CONTEXT_STATE_VALID()) {
NOTREACHED();
return; return;
}
if(!_Context->process()->CalledOnValidThread()) // Must always be called on the same thread as Initialize.
if(!_Context->process()->CalledOnValidThread()) {
NOTREACHED();
return; return;
}
_Context->process()->DoMessageLoopIteration(); _Context->process()->DoMessageLoopIteration();
} }
@ -104,8 +119,11 @@ static void UIT_RegisterPlugin(struct CefPluginInfo* plugin_info)
bool CefRegisterPlugin(const struct CefPluginInfo& plugin_info) bool CefRegisterPlugin(const struct CefPluginInfo& plugin_info)
{ {
if(!_Context.get()) // Verify that the context is in a valid state.
if (!CONTEXT_STATE_VALID()) {
NOTREACHED();
return false; return false;
}
CefPluginInfo* pPluginInfo = new CefPluginInfo; CefPluginInfo* pPluginInfo = new CefPluginInfo;
*pPluginInfo = plugin_info; *pPluginInfo = plugin_info;
@ -173,15 +191,15 @@ bool CefPostDelayedTask(CefThreadId threadId, CefRefPtr<CefTask> task,
// CefContext // CefContext
CefContext::CefContext() : process_(NULL) CefContext::CefContext() : initialized_(false), shutting_down_(false)
{ {
} }
CefContext::~CefContext() CefContext::~CefContext()
{ {
// Just in case CefShutdown() isn't called if(!shutting_down_)
Shutdown(); Shutdown();
} }
bool CefContext::Initialize(const CefSettings& settings, bool CefContext::Initialize(const CefSettings& settings,
@ -200,19 +218,38 @@ bool CefContext::Initialize(const CefSettings& settings,
process_ = new CefProcess(settings_.multi_threaded_message_loop); process_ = new CefProcess(settings_.multi_threaded_message_loop);
process_->CreateChildThreads(); process_->CreateChildThreads();
initialized_ = true;
return true; return true;
} }
void CefContext::Shutdown() void CefContext::Shutdown()
{ {
// Remove all existing browsers. // Must always be called on the same thread as Initialize.
//RemoveAllBrowsers(); DCHECK(process_->CalledOnValidThread());
// Deleting the process will destroy the child threads. shutting_down_ = true;
if(settings_.multi_threaded_message_loop) {
// Event that will be used to signal when shutdown is complete. Start in
// non-signaled mode so that the event will block.
base::WaitableEvent event(false, false);
// Finish shutdown on the UI thread.
CefThread::PostTask(CefThread::UI, FROM_HERE,
NewRunnableMethod(this, &CefContext::UIT_FinishShutdown, &event));
// Block until shutdown is complete.
event.Wait();
} else {
// Finish shutdown on the current thread, which should be the UI thread.
UIT_FinishShutdown(NULL);
}
// Delete the process to destroy the child threads.
process_ = NULL; process_ = NULL;
} }
bool CefContext::AddBrowser(CefRefPtr<CefBrowserImpl> browser) bool CefContext::AddBrowser(CefRefPtr<CefBrowserImpl> browser)
{ {
bool found = false; bool found = false;
@ -286,3 +323,27 @@ CefRefPtr<CefBrowserImpl> CefContext::GetBrowserByID(int id)
return browser; return browser;
} }
void CefContext::UIT_FinishShutdown(base::WaitableEvent* event)
{
DCHECK(CefThread::CurrentlyOn(CefThread::UI));
BrowserList list;
Lock();
if (!browserlist_.empty()) {
list = browserlist_;
browserlist_.clear();
}
Unlock();
// Destroy any remaining browser windows.
if (!list.empty()) {
BrowserList::iterator it = list.begin();
for(; it != list.end(); ++it)
(*it)->UIT_DestroyBrowser();
}
if(event)
event->Signal();
}

View File

@ -33,6 +33,12 @@ public:
const CefBrowserSettings& browser_defaults); const CefBrowserSettings& browser_defaults);
void Shutdown(); void Shutdown();
// Returns true if the context is initialized.
bool initialized() { return initialized_; }
// Returns true if the context is shutting down.
bool shutting_down() { return shutting_down_; }
scoped_refptr<CefProcess> process() { return process_; } scoped_refptr<CefProcess> process() { return process_; }
bool AddBrowser(CefRefPtr<CefBrowserImpl> browser); bool AddBrowser(CefRefPtr<CefBrowserImpl> browser);
@ -59,7 +65,17 @@ public:
{ storage_context_.reset(storage_context); } { storage_context_.reset(storage_context); }
DOMStorageContext* storage_context() { return storage_context_.get(); } DOMStorageContext* storage_context() { return storage_context_.get(); }
static bool ImplementsThreadSafeReferenceCounting() { return true; }
private: private:
// Performs shutdown actions that need to occur on the UI thread before any
// threads are destroyed.
void UIT_FinishShutdown(base::WaitableEvent* event);
// Track context state.
bool initialized_;
bool shutting_down_;
// Manages the various process threads. // Manages the various process threads.
scoped_refptr<CefProcess> process_; scoped_refptr<CefProcess> process_;
@ -80,7 +96,11 @@ private:
int next_browser_id_; int next_browser_id_;
}; };
// Global context object pointer // Global context object pointer.
extern CefRefPtr<CefContext> _Context; extern CefRefPtr<CefContext> _Context;
// Helper macro that returns true if the global context is in a valid state.
#define CONTEXT_STATE_VALID() \
(_Context.get() && _Context->initialized() && !_Context->shutting_down())
#endif // _CEF_CONTEXT_H #endif // _CEF_CONTEXT_H

View File

@ -393,9 +393,11 @@ bool CefRegisterScheme(const CefString& scheme_name,
const CefString& host_name, const CefString& host_name,
CefRefPtr<CefSchemeHandlerFactory> factory) CefRefPtr<CefSchemeHandlerFactory> factory)
{ {
// Verify that the context is already initialized // Verify that the context is in a valid state.
if(!_Context.get()) if (!CONTEXT_STATE_VALID()) {
NOTREACHED();
return false; return false;
}
// Use a smart pointer for the wrapper object because // Use a smart pointer for the wrapper object because
// RunnableMethodTraits::RetainCallee() (originating from NewRunnableMethod) // RunnableMethodTraits::RetainCallee() (originating from NewRunnableMethod)

View File

@ -143,12 +143,16 @@ bool CefRegisterExtension(const CefString& extension_name,
const CefString& javascript_code, const CefString& javascript_code,
CefRefPtr<CefV8Handler> handler) CefRefPtr<CefV8Handler> handler)
{ {
// Verify that the context is already initialized // Verify that the context is in a valid state.
if(!_Context.get()) if (!CONTEXT_STATE_VALID()) {
NOTREACHED();
return false; return false;
}
if(!handler.get()) if(!handler.get()) {
NOTREACHED();
return false; return false;
}
TrackString* name = new TrackString(extension_name); TrackString* name = new TrackString(extension_name);
TrackAdd(name); TrackAdd(name);