// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "content/public/test/nested_message_pump_android.h" #include "base/android/jni_android.h" #include "base/android/scoped_java_ref.h" #include "base/lazy_instance.h" #include "base/logging.h" #include "base/synchronization/waitable_event.h" #include "base/threading/thread_restrictions.h" #include "base/time/time.h" #include "jni/NestedSystemMessageHandler_jni.h" namespace { base::LazyInstance > g_message_handler_obj = LAZY_INSTANCE_INITIALIZER; } // namespace namespace content { struct NestedMessagePumpAndroid::RunState { RunState(base::MessagePump::Delegate* delegate, int run_depth) : delegate(delegate), run_depth(run_depth), should_quit(false), waitable_event(false, false) { } base::MessagePump::Delegate* delegate; // Used to count how many Run() invocations are on the stack. int run_depth; // Used to flag that the current Run() invocation should return ASAP. bool should_quit; // Used to sleep until there is more work to do. base::WaitableEvent waitable_event; // The time at which we should call DoDelayedWork. base::TimeTicks delayed_work_time; }; NestedMessagePumpAndroid::NestedMessagePumpAndroid() : state_(NULL) { } NestedMessagePumpAndroid::~NestedMessagePumpAndroid() { } void NestedMessagePumpAndroid::Run(Delegate* delegate) { RunState state(delegate, state_ ? state_->run_depth + 1 : 1); RunState* previous_state = state_; state_ = &state; JNIEnv* env = base::android::AttachCurrentThread(); DCHECK(env); // Need to cap the wait time to allow task processing on the java // side. Otherwise, a long wait time on the native will starve java // tasks. base::TimeDelta max_delay = base::TimeDelta::FromMilliseconds(100); for (;;) { if (state_->should_quit) break; bool did_work = state_->delegate->DoWork(); if (state_->should_quit) break; did_work |= state_->delegate->DoDelayedWork(&state_->delayed_work_time); if (state_->should_quit) break; if (did_work) { continue; } did_work = state_->delegate->DoIdleWork(); if (state_->should_quit) break; if (did_work) continue; // No native tasks to process right now. Process tasks from the Java // System message handler. This will return when the java message queue // is idle. bool ret = Java_NestedSystemMessageHandler_runNestedLoopTillIdle(env, g_message_handler_obj.Get().obj()); CHECK(ret) << "Error running java message loop, tests will likely fail."; base::ThreadRestrictions::ScopedAllowWait allow_wait; if (state_->delayed_work_time.is_null()) { state_->waitable_event.TimedWait(max_delay); } else { base::TimeDelta delay = state_->delayed_work_time - base::TimeTicks::Now(); if (delay > max_delay) delay = max_delay; if (delay > base::TimeDelta()) { state_->waitable_event.TimedWait(delay); } else { // It looks like delayed_work_time indicates a time in the past, so we // need to call DoDelayedWork now. state_->delayed_work_time = base::TimeTicks(); } } } state_ = previous_state; } void NestedMessagePumpAndroid::Start( base::MessagePump::Delegate* delegate) { JNIEnv* env = base::android::AttachCurrentThread(); DCHECK(env); g_message_handler_obj.Get().Reset( Java_NestedSystemMessageHandler_create(env)); } void NestedMessagePumpAndroid::Quit() { if (state_) { state_->should_quit = true; state_->waitable_event.Signal(); return; } } void NestedMessagePumpAndroid::ScheduleWork() { if (state_) { state_->waitable_event.Signal(); return; } } void NestedMessagePumpAndroid::ScheduleDelayedWork( const base::TimeTicks& delayed_work_time) { if (state_) { // We know that we can't be blocked on Wait right now since this method can // only be called on the same thread as Run, so we only need to update our // record of how long to sleep when we do sleep. state_->delayed_work_time = delayed_work_time; return; } } // static bool NestedMessagePumpAndroid::RegisterJni(JNIEnv* env) { return RegisterNativesImpl(env); } } // namespace content