1 // Copyright (c) 2016 The Chromium Embedded Framework Authors. All rights
2 // reserved. Use of this source code is governed by a BSD-style license that
3 // can be found in the LICENSE file.
4
5 #include "tests/shared/browser/main_message_loop_external_pump.h"
6
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <glib.h>
10 #include <math.h>
11
12 #include <memory>
13
14 #include "include/base/cef_logging.h"
15 #include "include/cef_app.h"
16
17 // From base/posix/eintr_wrapper.h.
18 // This provides a wrapper around system calls which may be interrupted by a
19 // signal and return EINTR. See man 7 signal.
20 // To prevent long-lasting loops (which would likely be a bug, such as a signal
21 // that should be masked) to go unnoticed, there is a limit after which the
22 // caller will nonetheless see an EINTR in Debug builds.
23 #if !defined(HANDLE_EINTR)
24 #if !DCHECK_IS_ON()
25
26 #define HANDLE_EINTR(x) \
27 ({ \
28 decltype(x) eintr_wrapper_result; \
29 do { \
30 eintr_wrapper_result = (x); \
31 } while (eintr_wrapper_result == -1 && errno == EINTR); \
32 eintr_wrapper_result; \
33 })
34
35 #else
36
37 #define HANDLE_EINTR(x) \
38 ({ \
39 int eintr_wrapper_counter = 0; \
40 decltype(x) eintr_wrapper_result; \
41 do { \
42 eintr_wrapper_result = (x); \
43 } while (eintr_wrapper_result == -1 && errno == EINTR && \
44 eintr_wrapper_counter++ < 100); \
45 eintr_wrapper_result; \
46 })
47
48 #endif // !DCHECK_IS_ON()
49 #endif // !defined(HANDLE_EINTR)
50
51 namespace client {
52
53 namespace {
54
55 class MainMessageLoopExternalPumpLinux : public MainMessageLoopExternalPump {
56 public:
57 MainMessageLoopExternalPumpLinux();
58 ~MainMessageLoopExternalPumpLinux();
59
60 // MainMessageLoopStd methods:
61 void Quit() override;
62 int Run() override;
63
64 // MainMessageLoopExternalPump methods:
65 void OnScheduleMessagePumpWork(int64 delay_ms) override;
66
67 // Internal methods used for processing the pump callbacks. They are public
68 // for simplicity but should not be used directly. HandlePrepare is called
69 // during the prepare step of glib, and returns a timeout that will be passed
70 // to the poll. HandleCheck is called after the poll has completed, and
71 // returns whether or not HandleDispatch should be called. HandleDispatch is
72 // called if HandleCheck returned true.
73 int HandlePrepare();
74 bool HandleCheck();
75 void HandleDispatch();
76
77 protected:
78 // MainMessageLoopExternalPump methods:
79 void SetTimer(int64 delay_ms) override;
80 void KillTimer() override;
81 bool IsTimerPending() override;
82
83 private:
84 // Used to flag that the Run() invocation should return ASAP.
85 bool should_quit_;
86
87 // A GLib structure that we can add event sources to. We use the default GLib
88 // context, which is the one to which all GTK events are dispatched.
89 GMainContext* context_;
90
91 // The work source. It is destroyed when the message pump is destroyed.
92 GSource* work_source_;
93
94 // The time when we need to do delayed work.
95 CefTime delayed_work_time_;
96
97 // We use a wakeup pipe to make sure we'll get out of the glib polling phase
98 // when another thread has scheduled us to do some work. There is a glib
99 // mechanism g_main_context_wakeup, but this won't guarantee that our event's
100 // Dispatch() will be called.
101 int wakeup_pipe_read_;
102 int wakeup_pipe_write_;
103
104 // Use a std::unique_ptr to avoid needing the definition of GPollFD in the
105 // header.
106 std::unique_ptr<GPollFD> wakeup_gpollfd_;
107 };
108
109 // Return a timeout suitable for the glib loop, -1 to block forever,
110 // 0 to return right away, or a timeout in milliseconds from now.
GetTimeIntervalMilliseconds(const CefTime & from)111 int GetTimeIntervalMilliseconds(const CefTime& from) {
112 if (from.GetDoubleT() == 0.0)
113 return -1;
114
115 CefTime now;
116 now.Now();
117
118 // Be careful here. CefTime has a precision of microseconds, but we want a
119 // value in milliseconds. If there are 5.5ms left, should the delay be 5 or
120 // 6? It should be 6 to avoid executing delayed work too early.
121 int delay =
122 static_cast<int>(ceil((from.GetDoubleT() - now.GetDoubleT()) * 1000.0));
123
124 // If this value is negative, then we need to run delayed work soon.
125 return delay < 0 ? 0 : delay;
126 }
127
128 struct WorkSource : public GSource {
129 MainMessageLoopExternalPumpLinux* pump;
130 };
131
WorkSourcePrepare(GSource * source,gint * timeout_ms)132 gboolean WorkSourcePrepare(GSource* source, gint* timeout_ms) {
133 *timeout_ms = static_cast<WorkSource*>(source)->pump->HandlePrepare();
134 // We always return FALSE, so that our timeout is honored. If we were
135 // to return TRUE, the timeout would be considered to be 0 and the poll
136 // would never block. Once the poll is finished, Check will be called.
137 return FALSE;
138 }
139
WorkSourceCheck(GSource * source)140 gboolean WorkSourceCheck(GSource* source) {
141 // Only return TRUE if Dispatch should be called.
142 return static_cast<WorkSource*>(source)->pump->HandleCheck();
143 }
144
WorkSourceDispatch(GSource * source,GSourceFunc unused_func,gpointer unused_data)145 gboolean WorkSourceDispatch(GSource* source,
146 GSourceFunc unused_func,
147 gpointer unused_data) {
148 static_cast<WorkSource*>(source)->pump->HandleDispatch();
149 // Always return TRUE so our source stays registered.
150 return TRUE;
151 }
152
153 // I wish these could be const, but g_source_new wants non-const.
154 GSourceFuncs WorkSourceFuncs = {WorkSourcePrepare, WorkSourceCheck,
155 WorkSourceDispatch, nullptr};
156
MainMessageLoopExternalPumpLinux()157 MainMessageLoopExternalPumpLinux::MainMessageLoopExternalPumpLinux()
158 : should_quit_(false),
159 context_(g_main_context_default()),
160 wakeup_gpollfd_(new GPollFD) {
161 // Create our wakeup pipe, which is used to flag when work was scheduled.
162 int fds[2];
163 int ret = pipe(fds);
164 DCHECK_EQ(ret, 0);
165 (void)ret; // Prevent warning in release mode.
166
167 wakeup_pipe_read_ = fds[0];
168 wakeup_pipe_write_ = fds[1];
169 wakeup_gpollfd_->fd = wakeup_pipe_read_;
170 wakeup_gpollfd_->events = G_IO_IN;
171
172 work_source_ = g_source_new(&WorkSourceFuncs, sizeof(WorkSource));
173 static_cast<WorkSource*>(work_source_)->pump = this;
174 g_source_add_poll(work_source_, wakeup_gpollfd_.get());
175 // Use a low priority so that we let other events in the queue go first.
176 g_source_set_priority(work_source_, G_PRIORITY_DEFAULT_IDLE);
177 // This is needed to allow Run calls inside Dispatch.
178 g_source_set_can_recurse(work_source_, TRUE);
179 g_source_attach(work_source_, context_);
180 }
181
~MainMessageLoopExternalPumpLinux()182 MainMessageLoopExternalPumpLinux::~MainMessageLoopExternalPumpLinux() {
183 g_source_destroy(work_source_);
184 g_source_unref(work_source_);
185 close(wakeup_pipe_read_);
186 close(wakeup_pipe_write_);
187 }
188
Quit()189 void MainMessageLoopExternalPumpLinux::Quit() {
190 should_quit_ = true;
191 }
192
Run()193 int MainMessageLoopExternalPumpLinux::Run() {
194 // We really only do a single task for each iteration of the loop. If we
195 // have done something, assume there is likely something more to do. This
196 // will mean that we don't block on the message pump until there was nothing
197 // more to do. We also set this to true to make sure not to block on the
198 // first iteration of the loop.
199 bool more_work_is_plausible = true;
200
201 // We run our own loop instead of using g_main_loop_quit in one of the
202 // callbacks. This is so we only quit our own loops, and we don't quit
203 // nested loops run by others.
204 for (;;) {
205 // Don't block if we think we have more work to do.
206 bool block = !more_work_is_plausible;
207
208 more_work_is_plausible = g_main_context_iteration(context_, block);
209 if (should_quit_)
210 break;
211 }
212
213 // We need to run the message pump until it is idle. However we don't have
214 // that information here so we run the message loop "for a while".
215 for (int i = 0; i < 10; ++i) {
216 // Do some work.
217 CefDoMessageLoopWork();
218
219 // Sleep to allow the CEF proc to do work.
220 usleep(50000);
221 }
222
223 return 0;
224 }
225
OnScheduleMessagePumpWork(int64 delay_ms)226 void MainMessageLoopExternalPumpLinux::OnScheduleMessagePumpWork(
227 int64 delay_ms) {
228 // This can be called on any thread, so we don't want to touch any state
229 // variables as we would then need locks all over. This ensures that if we
230 // are sleeping in a poll that we will wake up.
231 if (HANDLE_EINTR(write(wakeup_pipe_write_, &delay_ms, sizeof(int64))) !=
232 sizeof(int64)) {
233 NOTREACHED() << "Could not write to the UI message loop wakeup pipe!";
234 }
235 }
236
237 // Return the timeout we want passed to poll.
HandlePrepare()238 int MainMessageLoopExternalPumpLinux::HandlePrepare() {
239 // We don't think we have work to do, but make sure not to block longer than
240 // the next time we need to run delayed work.
241 return GetTimeIntervalMilliseconds(delayed_work_time_);
242 }
243
HandleCheck()244 bool MainMessageLoopExternalPumpLinux::HandleCheck() {
245 // We usually have a single message on the wakeup pipe, since we are only
246 // signaled when the queue went from empty to non-empty, but there can be
247 // two messages if a task posted a task, hence we read at most two bytes.
248 // The glib poll will tell us whether there was data, so this read shouldn't
249 // block.
250 if (wakeup_gpollfd_->revents & G_IO_IN) {
251 int64 delay_ms[2];
252 const size_t num_bytes =
253 HANDLE_EINTR(read(wakeup_pipe_read_, delay_ms, sizeof(int64) * 2));
254 if (num_bytes < sizeof(int64)) {
255 NOTREACHED() << "Error reading from the wakeup pipe.";
256 }
257 if (num_bytes == sizeof(int64))
258 OnScheduleWork(delay_ms[0]);
259 if (num_bytes == sizeof(int64) * 2)
260 OnScheduleWork(delay_ms[1]);
261 }
262
263 if (GetTimeIntervalMilliseconds(delayed_work_time_) == 0) {
264 // The timer has expired. That condition will stay true until we process
265 // that delayed work, so we don't need to record this differently.
266 return true;
267 }
268
269 return false;
270 }
271
HandleDispatch()272 void MainMessageLoopExternalPumpLinux::HandleDispatch() {
273 OnTimerTimeout();
274 }
275
SetTimer(int64 delay_ms)276 void MainMessageLoopExternalPumpLinux::SetTimer(int64 delay_ms) {
277 DCHECK_GT(delay_ms, 0);
278
279 CefTime now;
280 now.Now();
281
282 delayed_work_time_ =
283 CefTime(now.GetDoubleT() + static_cast<double>(delay_ms) / 1000.0);
284 }
285
KillTimer()286 void MainMessageLoopExternalPumpLinux::KillTimer() {
287 delayed_work_time_ = CefTime();
288 }
289
IsTimerPending()290 bool MainMessageLoopExternalPumpLinux::IsTimerPending() {
291 return GetTimeIntervalMilliseconds(delayed_work_time_) > 0;
292 }
293
294 } // namespace
295
296 // static
297 std::unique_ptr<MainMessageLoopExternalPump>
Create()298 MainMessageLoopExternalPump::Create() {
299 return std::make_unique<MainMessageLoopExternalPumpLinux>();
300 }
301
302 } // namespace client
303