blob: e3f5e68fc9dcfc356a920dc7ea80d1bc91f37978 [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
Qijiang Fan713061e2021-03-08 15:45:12 +09009#include <base/check.h>
10#include <base/check_op.h>
Eric Carusobcc58bb2019-11-07 14:10:35 -080011#include <base/threading/sequenced_task_runner_handle.h>
12
Eric Caruso246e1412019-01-24 16:44:02 -080013namespace glib_bridge {
14
15namespace {
16
Eric Caruso246e1412019-01-24 16:44:02 -080017struct GMainContextLock {
18 public:
19 explicit GMainContextLock(GMainContext* context) : context_(context) {
20 CHECK(context_);
Eric Carusobcc58bb2019-11-07 14:10:35 -080021 CHECK(g_main_context_acquire(context_));
Eric Caruso246e1412019-01-24 16:44:02 -080022 }
23
24 ~GMainContextLock() { g_main_context_release(context_); }
25
26 private:
Eric Carusobcc58bb2019-11-07 14:10:35 -080027 GMainContext* context_; // weak
Eric Caruso246e1412019-01-24 16:44:02 -080028};
29
Eric Carusobcc58bb2019-11-07 14:10:35 -080030} // namespace
31
32GlibBridge::GlibBridge()
33 : glib_context_(g_main_context_new()),
Eric Caruso246e1412019-01-24 16:44:02 -080034 state_(State::kPreparingIteration),
35 weak_ptr_factory_(this) {
Eric Carusobcc58bb2019-11-07 14:10:35 -080036 CHECK(glib_context_);
37 g_main_context_push_thread_default(glib_context_);
38 base::SequencedTaskRunnerHandle::Get()->PostTask(
Eric Caruso246e1412019-01-24 16:44:02 -080039 FROM_HERE, base::Bind(&GlibBridge::PrepareIteration,
40 weak_ptr_factory_.GetWeakPtr()));
41}
42
Eric Carusobcc58bb2019-11-07 14:10:35 -080043GlibBridge::~GlibBridge() {
44 g_main_context_pop_thread_default(glib_context_);
45 g_main_context_unref(glib_context_);
46}
Eric Carusof37003d2019-04-30 16:20:52 -070047
Eric Carusobcc58bb2019-11-07 14:10:35 -080048void GlibBridge::PrepareIteration() {
49 CHECK_EQ(state_, State::kPreparingIteration);
50 CHECK(watchers_.empty());
Eric Caruso246e1412019-01-24 16:44:02 -080051 GMainContextLock _l(glib_context_);
52
Eric Carusobcc58bb2019-11-07 14:10:35 -080053 bool immediate = g_main_context_prepare(glib_context_, &max_priority_);
Eric Caruso246e1412019-01-24 16:44:02 -080054
55 int num_fds =
56 g_main_context_query(glib_context_, max_priority_, nullptr, nullptr, 0);
57 poll_fds_ = std::vector<GPollFD>(num_fds);
Eric Caruso246e1412019-01-24 16:44:02 -080058
59 int timeout_ms;
60 g_main_context_query(glib_context_, max_priority_, &timeout_ms, &poll_fds_[0],
61 num_fds);
Eric Carusobcc58bb2019-11-07 14:10:35 -080062 if (immediate || (num_fds == 0 && timeout_ms == 0)) {
63 DVLOG(1) << "Iteration can be dispatched immediately";
64 base::SequencedTaskRunnerHandle::Get()->PostTask(
65 FROM_HERE,
66 base::Bind(&GlibBridge::Dispatch, weak_ptr_factory_.GetWeakPtr()));
67 state_ = State::kReadyForDispatch;
68 return;
69 }
Eric Carusof37003d2019-04-30 16:20:52 -070070
71 // Collect information about which poll flags we need for each fd.
72 std::map<int, int> poll_flags;
Eric Carusobcc58bb2019-11-07 14:10:35 -080073 for (GPollFD& poll_fd : poll_fds_) {
74 fd_map_[poll_fd.fd].push_back(&poll_fd);
Eric Carusof37003d2019-04-30 16:20:52 -070075 poll_flags[poll_fd.fd] |= poll_fd.events;
Eric Carusobcc58bb2019-11-07 14:10:35 -080076 }
77
78 DVLOG(1) << "Preparing iteration with timeout " << timeout_ms << " ms, "
79 << poll_flags.size() << " event FDs";
Eric Carusof37003d2019-04-30 16:20:52 -070080
81 for (const auto& fd_flags : poll_flags) {
Eric Carusobcc58bb2019-11-07 14:10:35 -080082 std::unique_ptr<base::FileDescriptorWatcher::Controller> reader;
83 if (fd_flags.second & G_IO_IN) {
84 reader = base::FileDescriptorWatcher::WatchReadable(
85 fd_flags.first,
86 base::Bind(&GlibBridge::OnEvent, weak_ptr_factory_.GetWeakPtr(),
87 fd_flags.first, G_IO_IN));
88 CHECK(reader) << "Could not set up read watcher for fd "
89 << fd_flags.first;
90 }
91
92 std::unique_ptr<base::FileDescriptorWatcher::Controller> writer;
93 if (fd_flags.second & G_IO_OUT) {
94 writer = base::FileDescriptorWatcher::WatchWritable(
95 fd_flags.first,
96 base::Bind(&GlibBridge::OnEvent, weak_ptr_factory_.GetWeakPtr(),
97 fd_flags.first, G_IO_OUT));
98 CHECK(writer) << "Could not set up write watcher for fd "
99 << fd_flags.first;
100 }
101
102 watchers_[fd_flags.first] = Watcher{std::move(reader), std::move(writer)};
Eric Caruso246e1412019-01-24 16:44:02 -0800103 }
104
105 state_ = State::kWaitingForEvents;
106 if (timeout_ms < 0)
107 return;
108
109 base::TimeDelta timeout = base::TimeDelta::FromMilliseconds(timeout_ms);
110 timeout_closure_.Reset(
Eric Carusobcc58bb2019-11-07 14:10:35 -0800111 base::Bind(&GlibBridge::Timeout, weak_ptr_factory_.GetWeakPtr()));
112 base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
Eric Caruso246e1412019-01-24 16:44:02 -0800113 FROM_HERE, timeout_closure_.callback(), timeout);
114}
115
116void GlibBridge::OnEvent(int fd, int flag) {
Eric Carusobcc58bb2019-11-07 14:10:35 -0800117 CHECK(state_ == State::kWaitingForEvents ||
118 state_ == State::kReadyForDispatch);
119 DVLOG(2) << "OnEvent(" << fd << ", " << flag << ")";
Eric Caruso246e1412019-01-24 16:44:02 -0800120 for (GPollFD* poll_fd : fd_map_[fd])
Eric Carusof37003d2019-04-30 16:20:52 -0700121 poll_fd->revents |= flag & poll_fd->events;
122
Eric Carusobcc58bb2019-11-07 14:10:35 -0800123 if (flag & G_IO_IN)
124 watchers_[fd].reader.reset();
125 if (flag & G_IO_OUT)
126 watchers_[fd].writer.reset();
Eric Caruso246e1412019-01-24 16:44:02 -0800127
Eric Caruso02913612020-07-07 17:40:31 -0700128 // Avoid posting the dispatch task if it's already posted
129 if (state_ == State::kReadyForDispatch)
130 return;
131
Eric Carusobcc58bb2019-11-07 14:10:35 -0800132 base::SequencedTaskRunnerHandle::Get()->PostTask(
133 FROM_HERE,
134 base::Bind(&GlibBridge::Dispatch, weak_ptr_factory_.GetWeakPtr()));
135 state_ = State::kReadyForDispatch;
136}
Eric Caruso246e1412019-01-24 16:44:02 -0800137
Eric Carusobcc58bb2019-11-07 14:10:35 -0800138void GlibBridge::Timeout() {
139 CHECK_EQ(state_, State::kWaitingForEvents);
140 base::SequencedTaskRunnerHandle::Get()->PostTask(
Eric Caruso246e1412019-01-24 16:44:02 -0800141 FROM_HERE,
142 base::Bind(&GlibBridge::Dispatch, weak_ptr_factory_.GetWeakPtr()));
143 state_ = State::kReadyForDispatch;
144}
145
146void GlibBridge::Dispatch() {
Eric Carusobcc58bb2019-11-07 14:10:35 -0800147 CHECK_EQ(state_, State::kReadyForDispatch);
Eric Caruso246e1412019-01-24 16:44:02 -0800148 GMainContextLock _l(glib_context_);
149
Eric Carusobcc58bb2019-11-07 14:10:35 -0800150 bool dispatched = g_main_context_check(glib_context_, max_priority_,
151 poll_fds_.data(), poll_fds_.size());
152 g_main_context_dispatch(glib_context_);
153 DVLOG(2) << (dispatched ? "Found" : "Did not find") << " source to dispatch";
154
Eric Caruso246e1412019-01-24 16:44:02 -0800155 timeout_closure_.Cancel();
156 watchers_.clear();
Eric Caruso246e1412019-01-24 16:44:02 -0800157 poll_fds_.clear();
158 max_priority_ = -1;
Eric Carusobcc58bb2019-11-07 14:10:35 -0800159 base::SequencedTaskRunnerHandle::Get()->PostTask(
Eric Caruso246e1412019-01-24 16:44:02 -0800160 FROM_HERE, base::Bind(&GlibBridge::PrepareIteration,
161 weak_ptr_factory_.GetWeakPtr()));
Eric Carusof37003d2019-04-30 16:20:52 -0700162 state_ = State::kPreparingIteration;
Eric Caruso246e1412019-01-24 16:44:02 -0800163}
164
Eric Caruso246e1412019-01-24 16:44:02 -0800165} // namespace glib_bridge