1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/public/test/nested_message_pump_android.h"
6
7#include "base/android/jni_android.h"
8#include "base/android/scoped_java_ref.h"
9#include "base/lazy_instance.h"
10#include "base/logging.h"
11#include "base/synchronization/waitable_event.h"
12#include "base/threading/thread_restrictions.h"
13#include "base/time/time.h"
14#include "jni/NestedSystemMessageHandler_jni.h"
15
16namespace {
17
18base::LazyInstance<base::android::ScopedJavaGlobalRef<jobject> >
19    g_message_handler_obj = LAZY_INSTANCE_INITIALIZER;
20
21}  // namespace
22
23
24namespace content {
25
26struct NestedMessagePumpAndroid::RunState {
27  RunState(base::MessagePump::Delegate* delegate, int run_depth)
28      : delegate(delegate),
29        run_depth(run_depth),
30        should_quit(false),
31        waitable_event(false, false) {
32  }
33
34  base::MessagePump::Delegate* delegate;
35
36  // Used to count how many Run() invocations are on the stack.
37  int run_depth;
38
39  // Used to flag that the current Run() invocation should return ASAP.
40  bool should_quit;
41
42  // Used to sleep until there is more work to do.
43  base::WaitableEvent waitable_event;
44
45  // The time at which we should call DoDelayedWork.
46  base::TimeTicks delayed_work_time;
47};
48
49NestedMessagePumpAndroid::NestedMessagePumpAndroid()
50    : state_(NULL) {
51}
52
53NestedMessagePumpAndroid::~NestedMessagePumpAndroid() {
54}
55
56void NestedMessagePumpAndroid::Run(Delegate* delegate) {
57  RunState state(delegate, state_ ? state_->run_depth + 1 : 1);
58  RunState* previous_state = state_;
59  state_ = &state;
60
61  JNIEnv* env = base::android::AttachCurrentThread();
62  DCHECK(env);
63
64  // Need to cap the wait time to allow task processing on the java
65  // side. Otherwise, a long wait time on the native will starve java
66  // tasks.
67  base::TimeDelta max_delay = base::TimeDelta::FromMilliseconds(100);
68
69  for (;;) {
70    if (state_->should_quit)
71      break;
72
73    bool did_work = state_->delegate->DoWork();
74    if (state_->should_quit)
75      break;
76
77    did_work |= state_->delegate->DoDelayedWork(&state_->delayed_work_time);
78    if (state_->should_quit)
79      break;
80
81    if (did_work) {
82      continue;
83    }
84
85    did_work = state_->delegate->DoIdleWork();
86    if (state_->should_quit)
87      break;
88
89    if (did_work)
90      continue;
91
92    // No native tasks to process right now. Process tasks from the Java
93    // System message handler. This will return when the java message queue
94    // is idle.
95    bool ret = Java_NestedSystemMessageHandler_runNestedLoopTillIdle(env,
96        g_message_handler_obj.Get().obj());
97    CHECK(ret) << "Error running java message loop, tests will likely fail.";
98
99    base::ThreadRestrictions::ScopedAllowWait allow_wait;
100    if (state_->delayed_work_time.is_null()) {
101      state_->waitable_event.TimedWait(max_delay);
102    } else {
103      base::TimeDelta delay =
104          state_->delayed_work_time - base::TimeTicks::Now();
105      if (delay > max_delay)
106        delay = max_delay;
107      if (delay > base::TimeDelta()) {
108        state_->waitable_event.TimedWait(delay);
109      } else {
110        // It looks like delayed_work_time indicates a time in the past, so we
111        // need to call DoDelayedWork now.
112        state_->delayed_work_time = base::TimeTicks();
113      }
114    }
115  }
116
117  state_ = previous_state;
118}
119
120void NestedMessagePumpAndroid::Start(
121    base::MessagePump::Delegate* delegate) {
122  JNIEnv* env = base::android::AttachCurrentThread();
123  DCHECK(env);
124  g_message_handler_obj.Get().Reset(
125      Java_NestedSystemMessageHandler_create(env));
126
127  base::MessagePumpForUI::Start(delegate);
128}
129
130void NestedMessagePumpAndroid::Quit() {
131  if (state_) {
132    state_->should_quit = true;
133    state_->waitable_event.Signal();
134    return;
135  }
136  base::MessagePumpForUI::Quit();
137}
138
139void NestedMessagePumpAndroid::ScheduleWork() {
140  if (state_) {
141    state_->waitable_event.Signal();
142    return;
143  }
144
145  base::MessagePumpForUI::ScheduleWork();
146}
147
148void NestedMessagePumpAndroid::ScheduleDelayedWork(
149    const base::TimeTicks& delayed_work_time) {
150  if (state_) {
151    // We know that we can't be blocked on Wait right now since this method can
152    // only be called on the same thread as Run, so we only need to update our
153    // record of how long to sleep when we do sleep.
154    state_->delayed_work_time = delayed_work_time;
155    return;
156  }
157
158  base::MessagePumpForUI::ScheduleDelayedWork(delayed_work_time);
159}
160
161// static
162bool NestedMessagePumpAndroid::RegisterJni(JNIEnv* env) {
163  return RegisterNativesImpl(env);
164}
165
166}  // namespace content
167