blob: 76d0b384123d1af4d4e24870ccc403b1c7031713 [file] [log] [blame]
// 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<base::android::ScopedJavaGlobalRef<jobject> >
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(base::WaitableEvent::ResetPolicy::AUTOMATIC,
base::WaitableEvent::InitialState::NOT_SIGNALED) {}
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;
}
}
} // namespace content