| // Copyright 2013 The Chromium Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "base/android/java_handler_thread.h" | |
| #include "base/android/jni_android.h" | |
| #include "base/android/jni_string.h" | |
| #include "base/functional/bind.h" | |
| #include "base/message_loop/message_pump.h" | |
| #include "base/message_loop/message_pump_type.h" | |
| #include "base/run_loop.h" | |
| #include "base/synchronization/waitable_event.h" | |
| #include "base/task/sequence_manager/sequence_manager_impl.h" | |
| #include "base/threading/platform_thread_internal_posix.h" | |
| #include "base/threading/thread_id_name_manager.h" | |
| #include "base/threading/thread_restrictions.h" | |
| // Must come after all headers that specialize FromJniType() / ToJniType(). | |
| #include "base/base_jni/JavaHandlerThread_jni.h" | |
| using base::android::AttachCurrentThread; | |
| namespace base { | |
| namespace android { | |
| JavaHandlerThread::JavaHandlerThread(const char* name, | |
| base::ThreadType thread_type) | |
| : JavaHandlerThread( | |
| name, | |
| Java_JavaHandlerThread_create( | |
| AttachCurrentThread(), | |
| name, | |
| base::internal::ThreadTypeToNiceValue(thread_type))) {} | |
| JavaHandlerThread::JavaHandlerThread( | |
| const char* name, | |
| const base::android::ScopedJavaLocalRef<jobject>& obj) | |
| : name_(name), java_thread_(obj) {} | |
| JavaHandlerThread::~JavaHandlerThread() { | |
| JNIEnv* env = base::android::AttachCurrentThread(); | |
| DCHECK(!Java_JavaHandlerThread_isAlive(env, java_thread_)); | |
| DCHECK(!state_ || state_->pump->IsAborted()); | |
| // TODO(mthiesse): We shouldn't leak the MessageLoop as this could affect | |
| // future tests. | |
| if (state_ && state_->pump->IsAborted()) { | |
| // When the Pump has been aborted due to a crash, we intentionally leak the | |
| // SequenceManager because the SequenceManager hasn't been shut down | |
| // properly and would trigger DCHECKS. This should only happen in tests, | |
| // where we handle the exception instead of letting it take down the | |
| // process. | |
| state_.release(); | |
| } | |
| } | |
| void JavaHandlerThread::Start() { | |
| // Check the thread has not already been started. | |
| DCHECK(!state_); | |
| JNIEnv* env = base::android::AttachCurrentThread(); | |
| base::WaitableEvent initialize_event( | |
| WaitableEvent::ResetPolicy::AUTOMATIC, | |
| WaitableEvent::InitialState::NOT_SIGNALED); | |
| Java_JavaHandlerThread_startAndInitialize( | |
| env, java_thread_, reinterpret_cast<intptr_t>(this), | |
| reinterpret_cast<intptr_t>(&initialize_event)); | |
| // Wait for thread to be initialized so it is ready to be used when Start | |
| // returns. | |
| base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope wait_allowed; | |
| initialize_event.Wait(); | |
| } | |
| void JavaHandlerThread::Stop() { | |
| DCHECK(!task_runner()->BelongsToCurrentThread()); | |
| task_runner()->PostTask( | |
| FROM_HERE, | |
| base::BindOnce(&JavaHandlerThread::StopOnThread, base::Unretained(this))); | |
| JNIEnv* env = base::android::AttachCurrentThread(); | |
| Java_JavaHandlerThread_joinThread(env, java_thread_); | |
| } | |
| void JavaHandlerThread::InitializeThread(JNIEnv* env, int64_t event) { | |
| base::ThreadIdNameManager::GetInstance()->RegisterThread( | |
| base::PlatformThread::CurrentHandle().platform_handle(), | |
| base::PlatformThread::CurrentId()); | |
| if (name_) { | |
| PlatformThread::SetName(name_); | |
| } | |
| thread_id_ = base::PlatformThread::CurrentId(); | |
| state_ = std::make_unique<State>(); | |
| #if DCHECK_IS_ON() | |
| initialized_ = true; | |
| #endif | |
| Init(); | |
| reinterpret_cast<base::WaitableEvent*>(event)->Signal(); | |
| } | |
| void JavaHandlerThread::OnLooperStopped(JNIEnv* env) { | |
| DCHECK(task_runner()->BelongsToCurrentThread()); | |
| state_.reset(); | |
| CleanUp(); | |
| base::ThreadIdNameManager::GetInstance()->RemoveName( | |
| base::PlatformThread::CurrentHandle().platform_handle(), | |
| base::PlatformThread::CurrentId()); | |
| } | |
| void JavaHandlerThread::StopSequenceManagerForTesting() { | |
| DCHECK(task_runner()->BelongsToCurrentThread()); | |
| StopOnThread(); | |
| } | |
| void JavaHandlerThread::JoinForTesting() { | |
| DCHECK(!task_runner()->BelongsToCurrentThread()); | |
| JNIEnv* env = base::android::AttachCurrentThread(); | |
| Java_JavaHandlerThread_joinThread(env, java_thread_); | |
| } | |
| void JavaHandlerThread::ListenForUncaughtExceptionsForTesting() { | |
| DCHECK(!task_runner()->BelongsToCurrentThread()); | |
| JNIEnv* env = base::android::AttachCurrentThread(); | |
| Java_JavaHandlerThread_listenForUncaughtExceptionsForTesting(env, | |
| java_thread_); | |
| } | |
| ScopedJavaLocalRef<jthrowable> JavaHandlerThread::GetUncaughtExceptionIfAny() { | |
| DCHECK(!task_runner()->BelongsToCurrentThread()); | |
| JNIEnv* env = base::android::AttachCurrentThread(); | |
| return Java_JavaHandlerThread_getUncaughtExceptionIfAny(env, java_thread_); | |
| } | |
| PlatformThreadId JavaHandlerThread::GetThreadId() const { | |
| #if DCHECK_IS_ON() | |
| DCHECK(initialized_); | |
| #endif | |
| return thread_id_; | |
| } | |
| void JavaHandlerThread::StopOnThread() { | |
| DCHECK(task_runner()->BelongsToCurrentThread()); | |
| DCHECK(state_); | |
| state_->pump->QuitWhenIdle(base::BindOnce( | |
| &JavaHandlerThread::QuitThreadSafely, base::Unretained(this))); | |
| } | |
| void JavaHandlerThread::QuitThreadSafely() { | |
| DCHECK(task_runner()->BelongsToCurrentThread()); | |
| JNIEnv* env = base::android::AttachCurrentThread(); | |
| Java_JavaHandlerThread_quitThreadSafely(env, java_thread_, | |
| reinterpret_cast<intptr_t>(this)); | |
| } | |
| JavaHandlerThread::State::State() | |
| : sequence_manager(sequence_manager::CreateUnboundSequenceManager( | |
| sequence_manager::SequenceManager::Settings::Builder() | |
| .SetMessagePumpType(base::MessagePumpType::JAVA) | |
| .Build())), | |
| default_task_queue( | |
| sequence_manager->CreateTaskQueue(sequence_manager::TaskQueue::Spec( | |
| sequence_manager::QueueName::DEFAULT_TQ))) { | |
| // TYPE_JAVA to get the Android java style message loop. | |
| std::unique_ptr<MessagePump> message_pump = | |
| MessagePump::Create(base::MessagePumpType::JAVA); | |
| pump = static_cast<MessagePumpForUI*>(message_pump.get()); | |
| // We must set SetDefaultTaskQueue before binding because the Android UI pump | |
| // creates a RunLoop which samples SingleThreadTaskRunner::GetCurrentDefault. | |
| sequence_manager->SetDefaultTaskQueue(default_task_queue.get()); | |
| sequence_manager->BindToMessagePump(std::move(message_pump)); | |
| } | |
| JavaHandlerThread::State::~State() = default; | |
| } // namespace android | |
| } // namespace base | |
| DEFINE_JNI(JavaHandlerThread) |