Commit d294ee3e authored by sievers's avatar sievers Committed by Commit bot

Android: Fix ChildThreadImpl::ShutdownThread()

Make this work independent for MsgLoop vs. ChildThread destruction
order and simplify the code a bit.

BUG=465397

Review URL: https://codereview.chromium.org/1140633005

Cr-Commit-Position: refs/heads/master@{#330655}
parent 8131d2b4
...@@ -59,6 +59,10 @@ ...@@ -59,6 +59,10 @@
#include "ipc/ipc_sync_message_filter.h" #include "ipc/ipc_sync_message_filter.h"
#include "ipc/mojo/ipc_channel_mojo.h" #include "ipc/mojo/ipc_channel_mojo.h"
#if defined(OS_ANDROID)
#include "base/thread_task_runner_handle.h"
#endif
#if defined(TCMALLOC_TRACE_MEMORY_SUPPORTED) #if defined(TCMALLOC_TRACE_MEMORY_SUPPORTED)
#include "third_party/tcmalloc/chromium/src/gperftools/heap-profiler.h" #include "third_party/tcmalloc/chromium/src/gperftools/heap-profiler.h"
#endif #endif
...@@ -161,40 +165,57 @@ class SuicideOnChannelErrorFilter : public IPC::MessageFilter { ...@@ -161,40 +165,57 @@ class SuicideOnChannelErrorFilter : public IPC::MessageFilter {
#endif // OS(POSIX) #endif // OS(POSIX)
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
ChildThreadImpl* g_child_thread = NULL; // A class that allows for triggering a clean shutdown from another
bool g_child_thread_initialized = false; // thread through draining the main thread's msg loop.
class QuitClosure {
public:
QuitClosure();
~QuitClosure();
// A lock protects g_child_thread. void BindToMainThread();
base::LazyInstance<base::Lock>::Leaky g_lazy_child_thread_lock = void PostQuitFromNonMainThread();
LAZY_INSTANCE_INITIALIZER;
// base::ConditionVariable has an explicit constructor that takes private:
// a base::Lock pointer as parameter. The base::DefaultLazyInstanceTraits static void PostClosure(
// doesn't handle the case. Thus, we need our own class here. const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
struct CondVarLazyInstanceTraits { base::Closure closure);
static const bool kRegisterOnExit = false;
#ifndef NDEBUG
static const bool kAllowedToAccessOnNonjoinableThread = true;
#endif
static base::ConditionVariable* New(void* instance) { base::Lock lock_;
return new (instance) base::ConditionVariable( base::ConditionVariable cond_var_;
g_lazy_child_thread_lock.Pointer()); base::Closure closure_;
}
static void Delete(base::ConditionVariable* instance) {
instance->~ConditionVariable();
}
}; };
// A condition variable that synchronize threads initializing and waiting QuitClosure::QuitClosure() : cond_var_(&lock_) {
// for g_child_thread. }
base::LazyInstance<base::ConditionVariable, CondVarLazyInstanceTraits>
g_lazy_child_thread_cv = LAZY_INSTANCE_INITIALIZER;
void QuitMainThreadMessageLoop() { QuitClosure::~QuitClosure() {
base::MessageLoop::current()->Quit(); }
void QuitClosure::PostClosure(
const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
base::Closure closure) {
task_runner->PostTask(FROM_HERE, closure);
}
void QuitClosure::BindToMainThread() {
base::AutoLock lock(lock_);
scoped_refptr<base::SingleThreadTaskRunner> task_runner(
base::ThreadTaskRunnerHandle::Get());
base::Closure quit_closure =
base::MessageLoop::current()->QuitWhenIdleClosure();
closure_ = base::Bind(&QuitClosure::PostClosure, task_runner, quit_closure);
cond_var_.Signal();
}
void QuitClosure::PostQuitFromNonMainThread() {
base::AutoLock lock(lock_);
while (closure_.is_null())
cond_var_.Wait();
closure_.Run();
} }
base::LazyInstance<QuitClosure> g_quit_closure = LAZY_INSTANCE_INITIALIZER;
#endif #endif
} // namespace } // namespace
...@@ -402,14 +423,7 @@ void ChildThreadImpl::Init(const Options& options) { ...@@ -402,14 +423,7 @@ void ChildThreadImpl::Init(const Options& options) {
base::TimeDelta::FromSeconds(connection_timeout)); base::TimeDelta::FromSeconds(connection_timeout));
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
{ g_quit_closure.Get().BindToMainThread();
base::AutoLock lock(g_lazy_child_thread_lock.Get());
g_child_thread = this;
g_child_thread_initialized = true;
}
// Signalling without locking is fine here because only
// one thread can wait on the condition variable.
g_lazy_child_thread_cv.Get().Signal();
#endif #endif
#if defined(TCMALLOC_TRACE_MEMORY_SUPPORTED) #if defined(TCMALLOC_TRACE_MEMORY_SUPPORTED)
...@@ -435,13 +449,6 @@ ChildThreadImpl::~ChildThreadImpl() { ...@@ -435,13 +449,6 @@ ChildThreadImpl::~ChildThreadImpl() {
// |thread_safe_sender_| is still valid. // |thread_safe_sender_| is still valid.
discardable_shared_memory_manager_.reset(); discardable_shared_memory_manager_.reset();
#if defined(OS_ANDROID)
{
base::AutoLock lock(g_lazy_child_thread_lock.Get());
g_child_thread = nullptr;
}
#endif
#ifdef IPC_MESSAGE_LOG_ENABLED #ifdef IPC_MESSAGE_LOG_ENABLED
IPC::Logging::GetInstance()->SetIPCSender(NULL); IPC::Logging::GetInstance()->SetIPCSender(NULL);
#endif #endif
...@@ -636,20 +643,7 @@ ChildThreadImpl* ChildThreadImpl::current() { ...@@ -636,20 +643,7 @@ ChildThreadImpl* ChildThreadImpl::current() {
void ChildThreadImpl::ShutdownThread() { void ChildThreadImpl::ShutdownThread() {
DCHECK(!ChildThreadImpl::current()) << DCHECK(!ChildThreadImpl::current()) <<
"this method should NOT be called from child thread itself"; "this method should NOT be called from child thread itself";
{ g_quit_closure.Get().PostQuitFromNonMainThread();
base::AutoLock lock(g_lazy_child_thread_lock.Get());
while (!g_child_thread_initialized)
g_lazy_child_thread_cv.Get().Wait();
// g_child_thread may already have been destructed while we didn't hold the
// lock.
if (!g_child_thread)
return;
DCHECK_NE(base::MessageLoop::current(), g_child_thread->message_loop());
g_child_thread->message_loop()->PostTask(
FROM_HERE, base::Bind(&QuitMainThreadMessageLoop));
}
} }
#endif #endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment