• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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