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

View File

@ -21,7 +21,7 @@ CefRefPtr<CefContext> _Context;
bool CefInitialize(const CefSettings& settings,
const CefBrowserSettings& browser_defaults)
{
// Return true if the context is already initialized
// Return true if the global context already exists.
if(_Context.get())
return true;
@ -31,33 +31,48 @@ bool CefInitialize(const CefSettings& settings,
return false;
}
// Create the new global context object
// Create the new global context object.
_Context = new CefContext();
// Initialize the global context
// Initialize the global context.
return _Context->Initialize(settings, browser_defaults);
}
void CefShutdown()
{
// Verify that the context is already initialized
if(!_Context.get())
// Verify that the context is in a valid state.
if (!CONTEXT_STATE_VALID()) {
NOTREACHED();
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();
// Delete the global context object
// Delete the global context object.
_Context = NULL;
}
void CefDoMessageLoopWork()
{
// Verify that the context is already initialized.
if(!_Context.get() || !_Context->process())
// Verify that the context is in a valid state.
if (!CONTEXT_STATE_VALID()) {
NOTREACHED();
return;
}
if(!_Context->process()->CalledOnValidThread())
// Must always be called on the same thread as Initialize.
if(!_Context->process()->CalledOnValidThread()) {
NOTREACHED();
return;
}
_Context->process()->DoMessageLoopIteration();
}
@ -104,8 +119,11 @@ static void UIT_RegisterPlugin(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;
}
CefPluginInfo* pPluginInfo = new CefPluginInfo;
*pPluginInfo = plugin_info;
@ -173,14 +191,14 @@ bool CefPostDelayedTask(CefThreadId threadId, CefRefPtr<CefTask> task,
// CefContext
CefContext::CefContext() : process_(NULL)
CefContext::CefContext() : initialized_(false), shutting_down_(false)
{
}
CefContext::~CefContext()
{
// Just in case CefShutdown() isn't called
if(!shutting_down_)
Shutdown();
}
@ -200,18 +218,37 @@ bool CefContext::Initialize(const CefSettings& settings,
process_ = new CefProcess(settings_.multi_threaded_message_loop);
process_->CreateChildThreads();
initialized_ = true;
return true;
}
void CefContext::Shutdown()
{
// Remove all existing browsers.
//RemoveAllBrowsers();
// Must always be called on the same thread as Initialize.
DCHECK(process_->CalledOnValidThread());
// Deleting the process will destroy the child threads.
process_ = NULL;
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;
}
bool CefContext::AddBrowser(CefRefPtr<CefBrowserImpl> browser)
{
@ -286,3 +323,27 @@ CefRefPtr<CefBrowserImpl> CefContext::GetBrowserByID(int id)
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);
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_; }
bool AddBrowser(CefRefPtr<CefBrowserImpl> browser);
@ -59,7 +65,17 @@ public:
{ storage_context_.reset(storage_context); }
DOMStorageContext* storage_context() { return storage_context_.get(); }
static bool ImplementsThreadSafeReferenceCounting() { return true; }
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.
scoped_refptr<CefProcess> process_;
@ -80,7 +96,11 @@ private:
int next_browser_id_;
};
// Global context object pointer
// Global context object pointer.
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

View File

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

View File

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