Home | History | Annotate | Download | only in message_loops
      1 // Copyright 2015 The Chromium OS 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 <brillo/message_loops/glib_message_loop.h>
      6 
      7 #include <fcntl.h>
      8 #include <unistd.h>
      9 
     10 #include <brillo/location_logging.h>
     11 
     12 using base::Closure;
     13 
     14 namespace brillo {
     15 
     16 GlibMessageLoop::GlibMessageLoop() {
     17   loop_ = g_main_loop_new(g_main_context_default(), FALSE);
     18 }
     19 
     20 GlibMessageLoop::~GlibMessageLoop() {
     21   // Cancel all pending tasks when destroying the message loop.
     22   for (const auto& task : tasks_) {
     23     DVLOG_LOC(task.second->location, 1)
     24         << "Removing task_id " << task.second->task_id
     25         << " leaked on GlibMessageLoop, scheduled from this location.";
     26     g_source_remove(task.second->source_id);
     27   }
     28   g_main_loop_unref(loop_);
     29 }
     30 
     31 MessageLoop::TaskId GlibMessageLoop::PostDelayedTask(
     32     const tracked_objects::Location& from_here,
     33     const Closure &task,
     34     base::TimeDelta delay) {
     35   TaskId task_id =  NextTaskId();
     36   // Note: While we store persistent = false in the ScheduledTask object, we
     37   // don't check it in OnRanPostedTask() since it is always false for delayed
     38   // tasks. This is only used for WatchFileDescriptor below.
     39   ScheduledTask* scheduled_task = new ScheduledTask{
     40     this, from_here, task_id, 0, false, std::move(task)};
     41   DVLOG_LOC(from_here, 1) << "Scheduling delayed task_id " << task_id
     42                           << " to run in " << delay << ".";
     43   scheduled_task->source_id = g_timeout_add_full(
     44       G_PRIORITY_DEFAULT,
     45       delay.InMillisecondsRoundedUp(),
     46       &GlibMessageLoop::OnRanPostedTask,
     47       reinterpret_cast<gpointer>(scheduled_task),
     48       DestroyPostedTask);
     49   tasks_[task_id] = scheduled_task;
     50   return task_id;
     51 }
     52 
     53 MessageLoop::TaskId GlibMessageLoop::WatchFileDescriptor(
     54     const tracked_objects::Location& from_here,
     55     int fd,
     56     WatchMode mode,
     57     bool persistent,
     58     const Closure &task) {
     59   // Quick check to see if the fd is valid.
     60   if (fcntl(fd, F_GETFD) == -1 && errno == EBADF)
     61       return MessageLoop::kTaskIdNull;
     62 
     63   GIOCondition condition = G_IO_NVAL;
     64   switch (mode) {
     65     case MessageLoop::kWatchRead:
     66       condition = static_cast<GIOCondition>(G_IO_IN | G_IO_HUP | G_IO_NVAL);
     67       break;
     68     case MessageLoop::kWatchWrite:
     69       condition = static_cast<GIOCondition>(G_IO_OUT | G_IO_HUP | G_IO_NVAL);
     70       break;
     71     default:
     72       return MessageLoop::kTaskIdNull;
     73   }
     74 
     75   // TODO(deymo): Used g_unix_fd_add_full() instead of g_io_add_watch_full()
     76   // when/if we switch to glib 2.36 or newer so we don't need to create a
     77   // GIOChannel for this.
     78   GIOChannel* io_channel = g_io_channel_unix_new(fd);
     79   if (!io_channel)
     80     return MessageLoop::kTaskIdNull;
     81   GError* error = nullptr;
     82   GIOStatus status = g_io_channel_set_encoding(io_channel, nullptr, &error);
     83   if (status != G_IO_STATUS_NORMAL) {
     84     LOG(ERROR) << "GError(" << error->code << "): "
     85                << (error->message ? error->message : "(unknown)");
     86     g_error_free(error);
     87     // g_io_channel_set_encoding() documentation states that this should be
     88     // valid in this context (a new io_channel), but enforce the check in
     89     // debug mode.
     90     DCHECK(status == G_IO_STATUS_NORMAL);
     91     return MessageLoop::kTaskIdNull;
     92   }
     93 
     94   TaskId task_id =  NextTaskId();
     95   ScheduledTask* scheduled_task = new ScheduledTask{
     96     this, from_here, task_id, 0, persistent, std::move(task)};
     97   scheduled_task->source_id = g_io_add_watch_full(
     98       io_channel,
     99       G_PRIORITY_DEFAULT,
    100       condition,
    101       &GlibMessageLoop::OnWatchedFdReady,
    102       reinterpret_cast<gpointer>(scheduled_task),
    103       DestroyPostedTask);
    104   // g_io_add_watch_full() increases the reference count on the newly created
    105   // io_channel, so we can dereference it now and it will be free'd once the
    106   // source is removed or now if g_io_add_watch_full() failed.
    107   g_io_channel_unref(io_channel);
    108 
    109   DVLOG_LOC(from_here, 1)
    110       << "Watching fd " << fd << " for "
    111       << (mode == MessageLoop::kWatchRead ? "reading" : "writing")
    112       << (persistent ? " persistently" : " just once")
    113       << " as task_id " << task_id
    114       << (scheduled_task->source_id ? " successfully" : " failed.");
    115 
    116   if (!scheduled_task->source_id) {
    117     delete scheduled_task;
    118     return MessageLoop::kTaskIdNull;
    119   }
    120   tasks_[task_id] = scheduled_task;
    121   return task_id;
    122 }
    123 
    124 bool GlibMessageLoop::CancelTask(TaskId task_id) {
    125   if (task_id == kTaskIdNull)
    126     return false;
    127   const auto task = tasks_.find(task_id);
    128   // It is a programmer error to attempt to remove a non-existent source.
    129   if (task == tasks_.end())
    130     return false;
    131   DVLOG_LOC(task->second->location, 1)
    132       << "Removing task_id " << task_id << " scheduled from this location.";
    133   guint source_id = task->second->source_id;
    134   // We remove here the entry from the tasks_ map, the pointer will be deleted
    135   // by the g_source_remove() call.
    136   tasks_.erase(task);
    137   return g_source_remove(source_id);
    138 }
    139 
    140 bool GlibMessageLoop::RunOnce(bool may_block) {
    141   return g_main_context_iteration(nullptr, may_block);
    142 }
    143 
    144 void GlibMessageLoop::Run() {
    145   g_main_loop_run(loop_);
    146 }
    147 
    148 void GlibMessageLoop::BreakLoop() {
    149   g_main_loop_quit(loop_);
    150 }
    151 
    152 MessageLoop::TaskId GlibMessageLoop::NextTaskId() {
    153   TaskId res;
    154   do {
    155     res = ++last_id_;
    156     // We would run out of memory before we run out of task ids.
    157   } while (!res || tasks_.find(res) != tasks_.end());
    158   return res;
    159 }
    160 
    161 gboolean GlibMessageLoop::OnRanPostedTask(gpointer user_data) {
    162   ScheduledTask* scheduled_task = reinterpret_cast<ScheduledTask*>(user_data);
    163   DVLOG_LOC(scheduled_task->location, 1)
    164       << "Running delayed task_id " << scheduled_task->task_id
    165       << " scheduled from this location.";
    166   // We only need to remove this task_id from the map. DestroyPostedTask will be
    167   // called with this same |user_data| where we can delete the ScheduledTask.
    168   scheduled_task->loop->tasks_.erase(scheduled_task->task_id);
    169   scheduled_task->closure.Run();
    170   return FALSE;  // Removes the source since a callback can only be called once.
    171 }
    172 
    173 gboolean GlibMessageLoop::OnWatchedFdReady(GIOChannel *source,
    174                                            GIOCondition condition,
    175                                            gpointer user_data) {
    176   ScheduledTask* scheduled_task = reinterpret_cast<ScheduledTask*>(user_data);
    177   DVLOG_LOC(scheduled_task->location, 1)
    178       << "Running task_id " << scheduled_task->task_id
    179       << " for watching a file descriptor, scheduled from this location.";
    180   if (!scheduled_task->persistent) {
    181     // We only need to remove this task_id from the map. DestroyPostedTask will
    182     // be called with this same |user_data| where we can delete the
    183     // ScheduledTask.
    184     scheduled_task->loop->tasks_.erase(scheduled_task->task_id);
    185   }
    186   scheduled_task->closure.Run();
    187   return scheduled_task->persistent;
    188 }
    189 
    190 void GlibMessageLoop::DestroyPostedTask(gpointer user_data) {
    191   delete reinterpret_cast<ScheduledTask*>(user_data);
    192 }
    193 
    194 }  // namespace brillo
    195