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
16 namespace {
17
18 base::LazyInstance<base::android::ScopedJavaGlobalRef<jobject> >
19 g_message_handler_obj = LAZY_INSTANCE_INITIALIZER;
20
21 } // namespace
22
23
24 namespace content {
25
26 struct NestedMessagePumpAndroid::RunState {
RunStatecontent::NestedMessagePumpAndroid::RunState27 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
NestedMessagePumpAndroid()49 NestedMessagePumpAndroid::NestedMessagePumpAndroid()
50 : state_(NULL) {
51 }
52
~NestedMessagePumpAndroid()53 NestedMessagePumpAndroid::~NestedMessagePumpAndroid() {
54 }
55
Run(Delegate * delegate)56 void 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
Start(base::MessagePump::Delegate * delegate)120 void 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
Quit()130 void NestedMessagePumpAndroid::Quit() {
131 if (state_) {
132 state_->should_quit = true;
133 state_->waitable_event.Signal();
134 return;
135 }
136 base::MessagePumpForUI::Quit();
137 }
138
ScheduleWork()139 void NestedMessagePumpAndroid::ScheduleWork() {
140 if (state_) {
141 state_->waitable_event.Signal();
142 return;
143 }
144
145 base::MessagePumpForUI::ScheduleWork();
146 }
147
ScheduleDelayedWork(const base::TimeTicks & delayed_work_time)148 void 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
RegisterJni(JNIEnv * env)162 bool NestedMessagePumpAndroid::RegisterJni(JNIEnv* env) {
163 return RegisterNativesImpl(env);
164 }
165
166 } // namespace content
167