diff --git a/libcef/browser/context.cc b/libcef/browser/context.cc index e9c8c0440..49711f163 100644 --- a/libcef/browser/context.cc +++ b/libcef/browser/context.cc @@ -13,13 +13,16 @@ #include "libcef/browser/thread_util.h" #include "libcef/browser/trace_subscriber.h" #include "libcef/common/main_delegate.h" +#include "libcef/renderer/content_renderer_client.h" #include "base/bind.h" #include "base/command_line.h" #include "base/file_util.h" #include "base/synchronization/waitable_event.h" +#include "base/threading/platform_thread.h" #include "content/public/app/content_main.h" #include "content/public/app/content_main_runner.h" +#include "content/public/browser/render_process_host.h" #include "content/public/common/content_switches.h" #include "ui/base/ui_base_switches.h" @@ -341,6 +344,14 @@ void CefContext::FinishShutdownOnUIThread( } void CefContext::FinalizeShutdown() { + if (content::RenderProcessHost::run_renderer_in_process()) { + // When running in single-process mode wait for the render thread to stop + // before continuing shutdown. Spin instead of using base::WaitableEvent + // because calling Wait() is not allowed on the UI thread. + while (!CefContentRendererClient::Get()->IsRenderThreadShutdownComplete()) + base::PlatformThread::YieldCurrentThread(); + } + // Shut down the browser runner or UI thread. main_delegate_->ShutdownBrowser(); diff --git a/libcef/renderer/content_renderer_client.cc b/libcef/renderer/content_renderer_client.cc index 4f17194a4..5ed7ccfcd 100644 --- a/libcef/renderer/content_renderer_client.cc +++ b/libcef/renderer/content_renderer_client.cc @@ -30,6 +30,7 @@ MSVC_POP_WARNING(); #include "base/path_service.h" #include "base/string_number_conversions.h" #include "content/common/child_thread.h" +#include "content/public/browser/render_process_host.h" #include "content/public/renderer/render_thread.h" #include "content/public/renderer/render_view.h" #include "ipc/ipc_sync_channel.h" @@ -208,7 +209,8 @@ struct CefContentRendererClient::SchemeInfo { CefContentRendererClient::CefContentRendererClient() : devtools_agent_count_(0), - uncaught_exception_stack_size_(0) { + uncaught_exception_stack_size_(0), + render_thread_shutdown_complete_(false) { } CefContentRendererClient::~CefContentRendererClient() { @@ -411,6 +413,12 @@ void CefContentRendererClient::RemoveWorkerTaskRunner(int worker_id) { worker_task_runner_map_.erase(it); } +bool CefContentRendererClient::IsRenderThreadShutdownComplete() { + DCHECK(content::RenderProcessHost::run_renderer_in_process()); + base::AutoLock lock_scope(render_thread_shutdown_lock_); + return render_thread_shutdown_complete_; +} + void CefContentRendererClient::RenderThreadStarted() { render_task_runner_ = base::MessageLoopProxy::current(); observer_.reset(new CefRenderProcessObserver()); @@ -419,6 +427,12 @@ void CefContentRendererClient::RenderThreadStarted() { thread->AddObserver(observer_.get()); thread->GetChannel()->AddFilter(new CefRenderMessageFilter); + if (content::RenderProcessHost::run_renderer_in_process()) { + // When running in single-process mode register as a destruction observer + // on the render thread's MessageLoop. + MessageLoop::current()->AddDestructionObserver(this); + } + // Note that under Linux, the media library will normally already have // been initialized by the Zygote before this instance became a Renderer. FilePath media_path; @@ -592,3 +606,9 @@ void CefContentRendererClient::WillReleaseScriptContext( CefV8ReleaseContext(context); } + +void CefContentRendererClient::WillDestroyCurrentMessageLoop() { + DCHECK(content::RenderProcessHost::run_renderer_in_process()); + base::AutoLock lock_scope(render_thread_shutdown_lock_); + render_thread_shutdown_complete_ = true; +} diff --git a/libcef/renderer/content_renderer_client.h b/libcef/renderer/content_renderer_client.h index 5a4e6e2d6..d8e3f1cf7 100644 --- a/libcef/renderer/content_renderer_client.h +++ b/libcef/renderer/content_renderer_client.h @@ -16,6 +16,7 @@ #include "base/compiler_specific.h" #include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" #include "base/sequenced_task_runner.h" #include "content/public/renderer/content_renderer_client.h" @@ -23,7 +24,8 @@ class CefRenderProcessObserver; class CefWebWorkerScriptObserver; struct Cef_CrossOriginWhiteListEntry_Params; -class CefContentRendererClient : public content::ContentRendererClient { +class CefContentRendererClient : public content::ContentRendererClient, + public MessageLoop::DestructionObserver { public: CefContentRendererClient(); virtual ~CefContentRendererClient(); @@ -72,6 +74,9 @@ class CefContentRendererClient : public content::ContentRendererClient { // Remove the task runner associated with the specified worker ID. void RemoveWorkerTaskRunner(int worker_id); + // Used in single-process mode to test when the RenderThread has stopped. + bool IsRenderThreadShutdownComplete(); + private: // ContentRendererClient implementation. virtual void RenderThreadStarted() OVERRIDE; @@ -89,6 +94,9 @@ class CefContentRendererClient : public content::ContentRendererClient { v8::Handle context, int world_id) OVERRIDE; + // MessageLoop::DestructionObserver implementation. + virtual void WillDestroyCurrentMessageLoop() OVERRIDE; + scoped_refptr render_task_runner_; scoped_ptr observer_; scoped_ptr worker_script_observer_; @@ -115,6 +123,11 @@ class CefContentRendererClient : public content::ContentRendererClient { WorkerTaskRunnerMap; WorkerTaskRunnerMap worker_task_runner_map_; base::Lock worker_task_runner_lock_; + + // Used in single-process mode to test when the RenderThread has stopped. + // Access must be protected by |render_thread_shutdown_lock_|. + bool render_thread_shutdown_complete_; + base::Lock render_thread_shutdown_lock_; }; #endif // CEF_LIBCEF_RENDERER_CONTENT_RENDERER_CLIENT_H_