diff --git a/cef3/libcef/browser/context.cc b/cef3/libcef/browser/context.cc index e9c8c0440..32e7338a4 100644 --- a/cef3/libcef/browser/context.cc +++ b/cef3/libcef/browser/context.cc @@ -13,6 +13,7 @@ #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" @@ -20,6 +21,7 @@ #include "base/synchronization/waitable_event.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 +343,11 @@ void CefContext::FinishShutdownOnUIThread( } void CefContext::FinalizeShutdown() { + if (content::RenderProcessHost::run_renderer_in_process()) { + // Blocks until RenderProcess cleanup is complete. + CefContentRendererClient::Get()->RunSingleProcessCleanup(); + } + // Shut down the browser runner or UI thread. main_delegate_->ShutdownBrowser(); diff --git a/cef3/libcef/renderer/content_renderer_client.cc b/cef3/libcef/renderer/content_renderer_client.cc index 4f17194a4..30f0bc917 100644 --- a/cef3/libcef/renderer/content_renderer_client.cc +++ b/cef3/libcef/renderer/content_renderer_client.cc @@ -14,6 +14,7 @@ MSVC_POP_WARNING(); #include "libcef/renderer/content_renderer_client.h" +#include "libcef/browser/context.h" #include "libcef/common/cef_messages.h" #include "libcef/common/cef_switches.h" #include "libcef/common/content_client.h" @@ -30,6 +31,8 @@ MSVC_POP_WARNING(); #include "base/path_service.h" #include "base/string_number_conversions.h" #include "content/common/child_thread.h" +#include "content/public/browser/browser_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 +211,8 @@ struct CefContentRendererClient::SchemeInfo { CefContentRendererClient::CefContentRendererClient() : devtools_agent_count_(0), - uncaught_exception_stack_size_(0) { + uncaught_exception_stack_size_(0), + single_process_cleanup_complete_(false) { } CefContentRendererClient::~CefContentRendererClient() { @@ -411,6 +415,35 @@ void CefContentRendererClient::RemoveWorkerTaskRunner(int worker_id) { worker_task_runner_map_.erase(it); } +void CefContentRendererClient::RunSingleProcessCleanup() { + DCHECK(content::RenderProcessHost::run_renderer_in_process()); + + // Make sure the render thread was actually started. + if (!render_task_runner_) + return; + + if (content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) { + RunSingleProcessCleanupOnUIThread(); + } else { + content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, + base::Bind(&CefContentRendererClient::RunSingleProcessCleanupOnUIThread, + base::Unretained(this))); + } + + // Wait for the render thread cleanup to complete. Spin instead of using + // base::WaitableEvent because calling Wait() is not allowed on the UI + // thread. + bool complete = false; + do { + { + base::AutoLock lock_scope(single_process_cleanup_lock_); + complete = single_process_cleanup_complete_; + } + if (!complete) + base::PlatformThread::YieldCurrentThread(); + } while (!complete); +} + void CefContentRendererClient::RenderThreadStarted() { render_task_runner_ = base::MessageLoopProxy::current(); observer_.reset(new CefRenderProcessObserver()); @@ -419,6 +452,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 +631,37 @@ void CefContentRendererClient::WillReleaseScriptContext( CefV8ReleaseContext(context); } + +void CefContentRendererClient::WillDestroyCurrentMessageLoop() { + base::AutoLock lock_scope(single_process_cleanup_lock_); + single_process_cleanup_complete_ = true; +} + +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 (!_Context->settings().multi_threaded_message_loop) + delete host; +} diff --git a/cef3/libcef/renderer/content_renderer_client.h b/cef3/libcef/renderer/content_renderer_client.h index 5a4e6e2d6..56e8ffb6c 100644 --- a/cef3/libcef/renderer/content_renderer_client.h +++ b/cef3/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,10 @@ class CefContentRendererClient : public content::ContentRendererClient { // Remove the task runner associated with the specified worker ID. void RemoveWorkerTaskRunner(int worker_id); + // Perform cleanup work that needs to occur before shutdown when running in + // single-process mode. Blocks until cleanup is complete. + void RunSingleProcessCleanup(); + private: // ContentRendererClient implementation. virtual void RenderThreadStarted() OVERRIDE; @@ -89,6 +95,12 @@ class CefContentRendererClient : public content::ContentRendererClient { v8::Handle context, int world_id) OVERRIDE; + // MessageLoop::DestructionObserver implementation. + virtual void WillDestroyCurrentMessageLoop() OVERRIDE; + + // Perform cleanup work for single-process mode. + void RunSingleProcessCleanupOnUIThread(); + scoped_refptr render_task_runner_; scoped_ptr observer_; scoped_ptr worker_script_observer_; @@ -115,6 +127,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 cleanup is complete. + // Access must be protected by |single_process_cleanup_lock_|. + bool single_process_cleanup_complete_; + base::Lock single_process_cleanup_lock_; }; #endif // CEF_LIBCEF_RENDERER_CONTENT_RENDERER_CLIENT_H_