summaryrefslogtreecommitdiffstats
path: root/base/message_pump_glib.cc
blob: 603c80eabef066ee1a89216f0dfbf5ee551e000f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
// Copyright (c) 2008 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 "base/message_pump_glib.h"

#include <fcntl.h>
#include <math.h>

#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/platform_thread.h"

namespace base {

static const char kWorkScheduled = '\0';
static const char kDelayedWorkScheduled = '\1';

// I wish these could be const, but g_source_new wants a non-const GSourceFunc
// pointer.

// static
GSourceFuncs MessagePumpForUI::WorkSourceFuncs = {
  WorkSourcePrepare,
  WorkSourceCheck,
  WorkSourceDispatch,
  NULL
};

// static
GSourceFuncs MessagePumpForUI::IdleSourceFuncs = {
  IdleSourcePrepare,
  IdleSourceCheck,
  IdleSourceDispatch,
  NULL
};

static int GetTimeIntervalMilliseconds(Time from) {
  if (from.is_null())
    return -1;

  // Be careful here.  TimeDelta has a precision of microseconds, but we want a
  // value in milliseconds.  If there are 5.5ms left, should the delay be 5 or
  // 6?  It should be 6 to avoid executing delayed work too early.
  double timeout = ceil((from - Time::Now()).InMillisecondsF());

  // If this value is negative, then we need to run delayed work soon.
  int delay = static_cast<int>(timeout);
  if (delay < 0)
    delay = 0;

  return delay;
}

MessagePumpForUI::MessagePumpForUI()
    : state_(NULL),
      context_(g_main_context_default()) {
  // Create a pipe with a non-blocking read end for use by ScheduleWork to
  // break us out of a poll.  Create the work source and attach the file
  // descriptor to it.
  int pipe_fd[2];
  CHECK(0 == pipe(pipe_fd)) << "Could not create pipe!";
  write_fd_work_scheduled_ = pipe_fd[1];
  read_fd_work_scheduled_ = pipe_fd[0];
  int flags = fcntl(read_fd_work_scheduled_, F_GETFL, 0);
  if (-1 == flags)
    flags = 0;
  CHECK(0 == fcntl(read_fd_work_scheduled_, F_SETFL, flags | O_NONBLOCK)) <<
      "Could not set file descriptor to non-blocking!";
  GPollFD poll_fd;
  poll_fd.fd = read_fd_work_scheduled_;
  poll_fd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;
  work_source_ = AddSource(&WorkSourceFuncs, G_PRIORITY_DEFAULT, &poll_fd);
}

MessagePumpForUI::~MessagePumpForUI() {
  close(read_fd_work_scheduled_);
  close(write_fd_work_scheduled_);
  g_source_destroy(work_source_);
  g_source_unref(work_source_);
}

struct ThreadIdTraits {
  static void New(void* instance) {
    int* thread_id = static_cast<int*>(instance);
    *thread_id = PlatformThread::CurrentId();
  }
  static void Delete(void* instance) {
  }
};

void MessagePumpForUI::Run(Delegate* delegate) {
  // Make sure we only run this on one thread.  GTK only has one message pump
  // so we can only have one UI loop per process.
  static LazyInstance<int, ThreadIdTraits> thread_id(base::LINKER_INITIALIZED);
  DCHECK(thread_id.Get() == PlatformThread::CurrentId()) <<
      "Running MessagePumpForUI on two different threads; "
      "this is unsupported by GLib!";

  RunState state;
  state.delegate = delegate;
  state.keep_running = true;
  // We emulate the behavior of MessagePumpDefault and try to do work at once.
  state.should_do_work = true;
  state.should_do_idle_work = false;
  state.idle_source = NULL;

  RunState* previous_state = state_;
  state_ = &state;

  while (state.keep_running)
    g_main_context_iteration(context_, true);

  if (state.idle_source) {
    // This removes the source from the context and releases GLib's hold on it.
    g_source_destroy(state.idle_source);
    // This releases our hold and destroys the source.
    g_source_unref(state.idle_source);
  }

  state_ = previous_state;
}

void MessagePumpForUI::Quit() {
  DCHECK(state_) << "Quit called outside Run!";
  state_->keep_running = false;
}

void MessagePumpForUI::ScheduleWork() {
  // This can be called on any thread, so we don't want to touch any state
  // variables as we would then need locks all over.  This ensures that if
  // we are sleeping in a poll that we will wake up, and we check the pipe
  // so we know when work was scheduled.
  CHECK(1 == write(write_fd_work_scheduled_, &kWorkScheduled, 1)) <<
      "Could not write to pipe!";
}

void MessagePumpForUI::ScheduleDelayedWork(const Time& delayed_work_time) {
  delayed_work_time_ = delayed_work_time;
  // This is an optimization.  Delayed work may not be imminent, we may just
  // need to update our timeout to poll.  Hence we don't want to go overkill
  // with kWorkScheduled.
  CHECK(1 == write(write_fd_work_scheduled_, &kDelayedWorkScheduled, 1)) <<
      "Could not write to pipe!";
}

// A brief refresher on GLib:
//     GLib sources have four callbacks: Prepare, Check, Dispatch and Finalize.
// On each iteration of the GLib pump, it calls each source's Prepare function.
// This function should return TRUE if it wants GLib to call its Dispatch, and
// FALSE otherwise.  It can also set a timeout in this case for the next time
// Prepare should be called again (it may be called sooner).
//     After the Prepare calls, GLib does a poll to check for events from the
// system.  File descriptors can be attached to the sources.  The poll may block
// if none of the Prepare calls returned TRUE.  It will block indefinitely, or
// by the minimum time returned by a source in Prepare.
//     After the poll, GLib calls Check for each source that returned FALSE
// from Prepare.  The return value of Check has the same meaning as for Prepare,
// making Check a second chance to tell GLib we are ready for Dispatch.
//     Finally, GLib calls Dispatch for each source that is ready.  If Dispatch
// returns FALSE, GLib will destroy the source.  Dispatch calls may be recursive
// (i.e., you can call Run from them), but Prepare and Check cannot.
//     Finalize is called when the source is destroyed.

// static
gboolean MessagePumpForUI::WorkSourcePrepare(GSource* source,
                                             gint* timeout_ms) {
  MessagePumpForUI* self = static_cast<WorkSource*>(source)->self;
  RunState* state = self->state_;

  if (state->should_do_work) {
    state->should_do_idle_work = false;
    *timeout_ms = 0;
    return TRUE;
  }

  *timeout_ms = GetTimeIntervalMilliseconds(self->delayed_work_time_);

  state->should_do_idle_work = true;
  // We want to do idle work right before poll goes to sleep.  Obviously
  // we are not currently asleep, but we may be about to since we have
  // no work to do.  If we don't have an idle source ready to go it's
  // probably because it fired already (or we just started and it hasn't
  // been added yet) and we should add one for when we are ready.  Note
  // that this new source will get Prepare called on this current pump
  // iteration since it gets added at the end of the source list.
  if (!state->idle_source) {
    state->idle_source =
        self->AddSource(&IdleSourceFuncs, G_PRIORITY_DEFAULT_IDLE, NULL);
  }

  return FALSE;
}

// static
gboolean MessagePumpForUI::WorkSourceCheck(GSource* source) {
  MessagePumpForUI* self = static_cast<WorkSource*>(source)->self;
  RunState* state = self->state_;

  // Make sure we don't attempt idle work until we are really sure we don't
  // have other work to do.  We'll know this in the call to Prepare.
  state->should_do_idle_work = false;

  // Check if ScheduleWork or ScheduleDelayedWork was called.  This is a
  // non-blocking read.
  char byte;
  while (0 < read(self->read_fd_work_scheduled_, &byte, 1)) {
    // Don't assume we actually have work yet unless the stronger ScheduleWork
    // was called.
    if (byte == kWorkScheduled)
      state->should_do_work = true;
  }

  if (state->should_do_work)
    return TRUE;

  if (!self->delayed_work_time_.is_null())
    return self->delayed_work_time_ <= Time::Now();

  return FALSE;
}

// static
gboolean MessagePumpForUI::WorkSourceDispatch(GSource* source,
                                              GSourceFunc unused_func,
                                              gpointer unused_data) {
  MessagePumpForUI* self = static_cast<WorkSource*>(source)->self;
  RunState* state = self->state_;
  DCHECK(!state->should_do_idle_work) <<
      "Idle work should not be flagged while regular work exists.";

  // Note that in this function we never return FALSE.  This source is owned
  // by GLib and shared by multiple calls to Run.  It will only finally get
  // destroyed when the loop is destroyed.

  state->should_do_work = state->delegate->DoWork();
  if (!state->keep_running)
    return TRUE;

  state->should_do_work |=
      state->delegate->DoDelayedWork(&self->delayed_work_time_);

  return TRUE;
}

// static
gboolean MessagePumpForUI::IdleSourcePrepare(GSource* source,
                                             gint* timeout_ms) {
  RunState* state = static_cast<WorkSource*>(source)->self->state_;
  *timeout_ms = 0;
  return state->should_do_idle_work;
}

// static
gboolean MessagePumpForUI::IdleSourceCheck(GSource* source) {
  RunState* state = static_cast<WorkSource*>(source)->self->state_;
  return state->should_do_idle_work;
}

// static
gboolean MessagePumpForUI::IdleSourceDispatch(GSource* source,
                                              GSourceFunc unused_func,
                                              gpointer unused_data) {
  RunState* state = static_cast<WorkSource*>(source)->self->state_;
  // We should not do idle work unless we didn't have other work to do.
  DCHECK(!state->should_do_work) << "Doing idle work in non-idle time!";
  state->should_do_idle_work = false;
  state->should_do_work = state->delegate->DoIdleWork();

  // This is an optimization.  We could always remove ourselves right now,
  // but we will just get re-added when WorkSourceCheck eventually returns
  // FALSE.
  if (!state->should_do_work) {
    // This is so that when we return FALSE, GLib will not only remove us
    // from the context, but since it holds the last reference, it will
    // destroy us as well.
    g_source_unref(source);
    state->idle_source = NULL;
  }

  return state->should_do_work;
}

GSource* MessagePumpForUI::AddSource(GSourceFuncs* funcs, gint priority,
                                     GPollFD *optional_poll_fd) {
  GSource* source = g_source_new(funcs, sizeof(WorkSource));

  // Setting the priority is actually a bit expensive since it causes GLib
  // to resort an internal list.
  if (priority != G_PRIORITY_DEFAULT)
    g_source_set_priority(source, priority);

  // This is needed to allow Run calls inside Dispatch.
  g_source_set_can_recurse(source, TRUE);
  static_cast<WorkSource*>(source)->self = this;

  if (optional_poll_fd)
    g_source_add_poll(source, optional_poll_fd);

  g_source_attach(source, context_);
  return source;
}

}  // namespace base