blob: 95900ed3b5e2e720ebdbcb6e31941ca9bf042091 [file] [log] [blame]
Eric Caruso246e1412019-01-24 16:44:02 -08001// Copyright 2019 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 "glib-bridge/glib_bridge.h"
6
Eric Carusobcc58bb2019-11-07 14:10:35 -08007#include <utility>
8
9#include <base/threading/sequenced_task_runner_handle.h>
10
Eric Caruso246e1412019-01-24 16:44:02 -080011namespace glib_bridge {
12
13namespace {
14
Eric Caruso246e1412019-01-24 16:44:02 -080015struct GMainContextLock {
16 public:
17 explicit GMainContextLock(GMainContext* context) : context_(context) {
18 CHECK(context_);
Eric Carusobcc58bb2019-11-07 14:10:35 -080019 CHECK(g_main_context_acquire(context_));
Eric Caruso246e1412019-01-24 16:44:02 -080020 }
21
22 ~GMainContextLock() { g_main_context_release(context_); }
23
24 private:
Eric Carusobcc58bb2019-11-07 14:10:35 -080025 GMainContext* context_; // weak
Eric Caruso246e1412019-01-24 16:44:02 -080026};
27
Eric Carusobcc58bb2019-11-07 14:10:35 -080028} // namespace
29
30GlibBridge::GlibBridge()
31 : glib_context_(g_main_context_new()),
Eric Caruso246e1412019-01-24 16:44:02 -080032 state_(State::kPreparingIteration),
33 weak_ptr_factory_(this) {
Eric Carusobcc58bb2019-11-07 14:10:35 -080034 CHECK(glib_context_);
35 g_main_context_push_thread_default(glib_context_);
36 base::SequencedTaskRunnerHandle::Get()->PostTask(
Eric Caruso246e1412019-01-24 16:44:02 -080037 FROM_HERE, base::Bind(&GlibBridge::PrepareIteration,
38 weak_ptr_factory_.GetWeakPtr()));
39}
40
Eric Carusobcc58bb2019-11-07 14:10:35 -080041GlibBridge::~GlibBridge() {
42 g_main_context_pop_thread_default(glib_context_);
43 g_main_context_unref(glib_context_);
44}
Eric Carusof37003d2019-04-30 16:20:52 -070045
Eric Carusobcc58bb2019-11-07 14:10:35 -080046void GlibBridge::PrepareIteration() {
47 CHECK_EQ(state_, State::kPreparingIteration);
48 CHECK(watchers_.empty());
Eric Caruso246e1412019-01-24 16:44:02 -080049 GMainContextLock _l(glib_context_);
50
Eric Carusobcc58bb2019-11-07 14:10:35 -080051 bool immediate = g_main_context_prepare(glib_context_, &max_priority_);
Eric Caruso246e1412019-01-24 16:44:02 -080052
53 int num_fds =
54 g_main_context_query(glib_context_, max_priority_, nullptr, nullptr, 0);
55 poll_fds_ = std::vector<GPollFD>(num_fds);
Eric Caruso246e1412019-01-24 16:44:02 -080056
57 int timeout_ms;
58 g_main_context_query(glib_context_, max_priority_, &timeout_ms, &poll_fds_[0],
59 num_fds);
Eric Carusobcc58bb2019-11-07 14:10:35 -080060 if (immediate || (num_fds == 0 && timeout_ms == 0)) {
61 DVLOG(1) << "Iteration can be dispatched immediately";
62 base::SequencedTaskRunnerHandle::Get()->PostTask(
63 FROM_HERE,
64 base::Bind(&GlibBridge::Dispatch, weak_ptr_factory_.GetWeakPtr()));
65 state_ = State::kReadyForDispatch;
66 return;
67 }
Eric Carusof37003d2019-04-30 16:20:52 -070068
69 // Collect information about which poll flags we need for each fd.
70 std::map<int, int> poll_flags;
Eric Carusobcc58bb2019-11-07 14:10:35 -080071 for (GPollFD& poll_fd : poll_fds_) {
72 fd_map_[poll_fd.fd].push_back(&poll_fd);
Eric Carusof37003d2019-04-30 16:20:52 -070073 poll_flags[poll_fd.fd] |= poll_fd.events;
Eric Carusobcc58bb2019-11-07 14:10:35 -080074 }
75
76 DVLOG(1) << "Preparing iteration with timeout " << timeout_ms << " ms, "
77 << poll_flags.size() << " event FDs";
Eric Carusof37003d2019-04-30 16:20:52 -070078
79 for (const auto& fd_flags : poll_flags) {
Eric Carusobcc58bb2019-11-07 14:10:35 -080080 std::unique_ptr<base::FileDescriptorWatcher::Controller> reader;
81 if (fd_flags.second & G_IO_IN) {
82 reader = base::FileDescriptorWatcher::WatchReadable(
83 fd_flags.first,
84 base::Bind(&GlibBridge::OnEvent, weak_ptr_factory_.GetWeakPtr(),
85 fd_flags.first, G_IO_IN));
86 CHECK(reader) << "Could not set up read watcher for fd "
87 << fd_flags.first;
88 }
89
90 std::unique_ptr<base::FileDescriptorWatcher::Controller> writer;
91 if (fd_flags.second & G_IO_OUT) {
92 writer = base::FileDescriptorWatcher::WatchWritable(
93 fd_flags.first,
94 base::Bind(&GlibBridge::OnEvent, weak_ptr_factory_.GetWeakPtr(),
95 fd_flags.first, G_IO_OUT));
96 CHECK(writer) << "Could not set up write watcher for fd "
97 << fd_flags.first;
98 }
99
100 watchers_[fd_flags.first] = Watcher{std::move(reader), std::move(writer)};
Eric Caruso246e1412019-01-24 16:44:02 -0800101 }
102
103 state_ = State::kWaitingForEvents;
104 if (timeout_ms < 0)
105 return;
106
107 base::TimeDelta timeout = base::TimeDelta::FromMilliseconds(timeout_ms);
108 timeout_closure_.Reset(
Eric Carusobcc58bb2019-11-07 14:10:35 -0800109 base::Bind(&GlibBridge::Timeout, weak_ptr_factory_.GetWeakPtr()));
110 base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
Eric Caruso246e1412019-01-24 16:44:02 -0800111 FROM_HERE, timeout_closure_.callback(), timeout);
112}
113
114void GlibBridge::OnEvent(int fd, int flag) {
Eric Carusobcc58bb2019-11-07 14:10:35 -0800115 CHECK(state_ == State::kWaitingForEvents ||
116 state_ == State::kReadyForDispatch);
117 DVLOG(2) << "OnEvent(" << fd << ", " << flag << ")";
Eric Caruso246e1412019-01-24 16:44:02 -0800118 for (GPollFD* poll_fd : fd_map_[fd])
Eric Carusof37003d2019-04-30 16:20:52 -0700119 poll_fd->revents |= flag & poll_fd->events;
120
Eric Carusobcc58bb2019-11-07 14:10:35 -0800121 if (flag & G_IO_IN)
122 watchers_[fd].reader.reset();
123 if (flag & G_IO_OUT)
124 watchers_[fd].writer.reset();
Eric Caruso246e1412019-01-24 16:44:02 -0800125
Eric Carusobcc58bb2019-11-07 14:10:35 -0800126 base::SequencedTaskRunnerHandle::Get()->PostTask(
127 FROM_HERE,
128 base::Bind(&GlibBridge::Dispatch, weak_ptr_factory_.GetWeakPtr()));
129 state_ = State::kReadyForDispatch;
130}
Eric Caruso246e1412019-01-24 16:44:02 -0800131
Eric Carusobcc58bb2019-11-07 14:10:35 -0800132void GlibBridge::Timeout() {
133 CHECK_EQ(state_, State::kWaitingForEvents);
134 base::SequencedTaskRunnerHandle::Get()->PostTask(
Eric Caruso246e1412019-01-24 16:44:02 -0800135 FROM_HERE,
136 base::Bind(&GlibBridge::Dispatch, weak_ptr_factory_.GetWeakPtr()));
137 state_ = State::kReadyForDispatch;
138}
139
140void GlibBridge::Dispatch() {
Eric Carusobcc58bb2019-11-07 14:10:35 -0800141 CHECK_EQ(state_, State::kReadyForDispatch);
Eric Caruso246e1412019-01-24 16:44:02 -0800142 GMainContextLock _l(glib_context_);
143
Eric Carusobcc58bb2019-11-07 14:10:35 -0800144 bool dispatched = g_main_context_check(glib_context_, max_priority_,
145 poll_fds_.data(), poll_fds_.size());
146 g_main_context_dispatch(glib_context_);
147 DVLOG(2) << (dispatched ? "Found" : "Did not find") << " source to dispatch";
148
Eric Caruso246e1412019-01-24 16:44:02 -0800149 timeout_closure_.Cancel();
150 watchers_.clear();
Eric Caruso246e1412019-01-24 16:44:02 -0800151 poll_fds_.clear();
152 max_priority_ = -1;
Eric Carusobcc58bb2019-11-07 14:10:35 -0800153 base::SequencedTaskRunnerHandle::Get()->PostTask(
Eric Caruso246e1412019-01-24 16:44:02 -0800154 FROM_HERE, base::Bind(&GlibBridge::PrepareIteration,
155 weak_ptr_factory_.GetWeakPtr()));
Eric Carusof37003d2019-04-30 16:20:52 -0700156 state_ = State::kPreparingIteration;
Eric Caruso246e1412019-01-24 16:44:02 -0800157}
158
Eric Caruso246e1412019-01-24 16:44:02 -0800159} // namespace glib_bridge