aboutsummaryrefslogtreecommitdiffstats
path: root/brillo/message_loops/glib_message_loop.cc
diff options
context:
space:
mode:
Diffstat (limited to 'brillo/message_loops/glib_message_loop.cc')
-rw-r--r--brillo/message_loops/glib_message_loop.cc194
1 files changed, 194 insertions, 0 deletions
diff --git a/brillo/message_loops/glib_message_loop.cc b/brillo/message_loops/glib_message_loop.cc
new file mode 100644
index 0000000..20c271d
--- /dev/null
+++ b/brillo/message_loops/glib_message_loop.cc
@@ -0,0 +1,194 @@
+// Copyright 2015 The Chromium OS 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 <brillo/message_loops/glib_message_loop.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <brillo/location_logging.h>
+
+using base::Closure;
+
+namespace brillo {
+
+GlibMessageLoop::GlibMessageLoop() {
+ loop_ = g_main_loop_new(g_main_context_default(), FALSE);
+}
+
+GlibMessageLoop::~GlibMessageLoop() {
+ // Cancel all pending tasks when destroying the message loop.
+ for (const auto& task : tasks_) {
+ DVLOG_LOC(task.second->location, 1)
+ << "Removing task_id " << task.second->task_id
+ << " leaked on GlibMessageLoop, scheduled from this location.";
+ g_source_remove(task.second->source_id);
+ }
+ g_main_loop_unref(loop_);
+}
+
+MessageLoop::TaskId GlibMessageLoop::PostDelayedTask(
+ const tracked_objects::Location& from_here,
+ const Closure &task,
+ base::TimeDelta delay) {
+ TaskId task_id = NextTaskId();
+ // Note: While we store persistent = false in the ScheduledTask object, we
+ // don't check it in OnRanPostedTask() since it is always false for delayed
+ // tasks. This is only used for WatchFileDescriptor below.
+ ScheduledTask* scheduled_task = new ScheduledTask{
+ this, from_here, task_id, 0, false, std::move(task)};
+ DVLOG_LOC(from_here, 1) << "Scheduling delayed task_id " << task_id
+ << " to run in " << delay << ".";
+ scheduled_task->source_id = g_timeout_add_full(
+ G_PRIORITY_DEFAULT,
+ delay.InMillisecondsRoundedUp(),
+ &GlibMessageLoop::OnRanPostedTask,
+ reinterpret_cast<gpointer>(scheduled_task),
+ DestroyPostedTask);
+ tasks_[task_id] = scheduled_task;
+ return task_id;
+}
+
+MessageLoop::TaskId GlibMessageLoop::WatchFileDescriptor(
+ const tracked_objects::Location& from_here,
+ int fd,
+ WatchMode mode,
+ bool persistent,
+ const Closure &task) {
+ // Quick check to see if the fd is valid.
+ if (fcntl(fd, F_GETFD) == -1 && errno == EBADF)
+ return MessageLoop::kTaskIdNull;
+
+ GIOCondition condition = G_IO_NVAL;
+ switch (mode) {
+ case MessageLoop::kWatchRead:
+ condition = static_cast<GIOCondition>(G_IO_IN | G_IO_HUP | G_IO_NVAL);
+ break;
+ case MessageLoop::kWatchWrite:
+ condition = static_cast<GIOCondition>(G_IO_OUT | G_IO_HUP | G_IO_NVAL);
+ break;
+ default:
+ return MessageLoop::kTaskIdNull;
+ }
+
+ // TODO(deymo): Used g_unix_fd_add_full() instead of g_io_add_watch_full()
+ // when/if we switch to glib 2.36 or newer so we don't need to create a
+ // GIOChannel for this.
+ GIOChannel* io_channel = g_io_channel_unix_new(fd);
+ if (!io_channel)
+ return MessageLoop::kTaskIdNull;
+ GError* error = nullptr;
+ GIOStatus status = g_io_channel_set_encoding(io_channel, nullptr, &error);
+ if (status != G_IO_STATUS_NORMAL) {
+ LOG(ERROR) << "GError(" << error->code << "): "
+ << (error->message ? error->message : "(unknown)");
+ g_error_free(error);
+ // g_io_channel_set_encoding() documentation states that this should be
+ // valid in this context (a new io_channel), but enforce the check in
+ // debug mode.
+ DCHECK(status == G_IO_STATUS_NORMAL);
+ return MessageLoop::kTaskIdNull;
+ }
+
+ TaskId task_id = NextTaskId();
+ ScheduledTask* scheduled_task = new ScheduledTask{
+ this, from_here, task_id, 0, persistent, std::move(task)};
+ scheduled_task->source_id = g_io_add_watch_full(
+ io_channel,
+ G_PRIORITY_DEFAULT,
+ condition,
+ &GlibMessageLoop::OnWatchedFdReady,
+ reinterpret_cast<gpointer>(scheduled_task),
+ DestroyPostedTask);
+ // g_io_add_watch_full() increases the reference count on the newly created
+ // io_channel, so we can dereference it now and it will be free'd once the
+ // source is removed or now if g_io_add_watch_full() failed.
+ g_io_channel_unref(io_channel);
+
+ DVLOG_LOC(from_here, 1)
+ << "Watching fd " << fd << " for "
+ << (mode == MessageLoop::kWatchRead ? "reading" : "writing")
+ << (persistent ? " persistently" : " just once")
+ << " as task_id " << task_id
+ << (scheduled_task->source_id ? " successfully" : " failed.");
+
+ if (!scheduled_task->source_id) {
+ delete scheduled_task;
+ return MessageLoop::kTaskIdNull;
+ }
+ tasks_[task_id] = scheduled_task;
+ return task_id;
+}
+
+bool GlibMessageLoop::CancelTask(TaskId task_id) {
+ if (task_id == kTaskIdNull)
+ return false;
+ const auto task = tasks_.find(task_id);
+ // It is a programmer error to attempt to remove a non-existent source.
+ if (task == tasks_.end())
+ return false;
+ DVLOG_LOC(task->second->location, 1)
+ << "Removing task_id " << task_id << " scheduled from this location.";
+ guint source_id = task->second->source_id;
+ // We remove here the entry from the tasks_ map, the pointer will be deleted
+ // by the g_source_remove() call.
+ tasks_.erase(task);
+ return g_source_remove(source_id);
+}
+
+bool GlibMessageLoop::RunOnce(bool may_block) {
+ return g_main_context_iteration(nullptr, may_block);
+}
+
+void GlibMessageLoop::Run() {
+ g_main_loop_run(loop_);
+}
+
+void GlibMessageLoop::BreakLoop() {
+ g_main_loop_quit(loop_);
+}
+
+MessageLoop::TaskId GlibMessageLoop::NextTaskId() {
+ TaskId res;
+ do {
+ res = ++last_id_;
+ // We would run out of memory before we run out of task ids.
+ } while (!res || tasks_.find(res) != tasks_.end());
+ return res;
+}
+
+gboolean GlibMessageLoop::OnRanPostedTask(gpointer user_data) {
+ ScheduledTask* scheduled_task = reinterpret_cast<ScheduledTask*>(user_data);
+ DVLOG_LOC(scheduled_task->location, 1)
+ << "Running delayed task_id " << scheduled_task->task_id
+ << " scheduled from this location.";
+ // We only need to remove this task_id from the map. DestroyPostedTask will be
+ // called with this same |user_data| where we can delete the ScheduledTask.
+ scheduled_task->loop->tasks_.erase(scheduled_task->task_id);
+ scheduled_task->closure.Run();
+ return FALSE; // Removes the source since a callback can only be called once.
+}
+
+gboolean GlibMessageLoop::OnWatchedFdReady(GIOChannel *source,
+ GIOCondition condition,
+ gpointer user_data) {
+ ScheduledTask* scheduled_task = reinterpret_cast<ScheduledTask*>(user_data);
+ DVLOG_LOC(scheduled_task->location, 1)
+ << "Running task_id " << scheduled_task->task_id
+ << " for watching a file descriptor, scheduled from this location.";
+ if (!scheduled_task->persistent) {
+ // We only need to remove this task_id from the map. DestroyPostedTask will
+ // be called with this same |user_data| where we can delete the
+ // ScheduledTask.
+ scheduled_task->loop->tasks_.erase(scheduled_task->task_id);
+ }
+ scheduled_task->closure.Run();
+ return scheduled_task->persistent;
+}
+
+void GlibMessageLoop::DestroyPostedTask(gpointer user_data) {
+ delete reinterpret_cast<ScheduledTask*>(user_data);
+}
+
+} // namespace brillo