blob: ad9dfbf7bbe0b4828a63c93440e8422a67c1d0bc [file] [log] [blame]
// Copyright 2019 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 "glib-bridge/glib_bridge.h"
namespace glib_bridge {
namespace {
base::MessageLoopForIO::Mode ConvertGPollFlags(int flags) {
if ((flags & (G_IO_IN | G_IO_OUT)) == (G_IO_IN | G_IO_OUT))
return base::MessageLoopForIO::WATCH_READ_WRITE;
else if ((flags & G_IO_IN) == G_IO_IN)
return base::MessageLoopForIO::WATCH_READ;
else if ((flags & G_IO_OUT) == G_IO_OUT)
return base::MessageLoopForIO::WATCH_WRITE;
NOTREACHED();
return base::MessageLoopForIO::WATCH_READ;
}
} // namespace
struct GMainContextLock {
public:
explicit GMainContextLock(GMainContext* context) : context_(context) {
CHECK(context_);
CHECK_EQ(g_main_context_acquire(context_), TRUE);
}
~GMainContextLock() { g_main_context_release(context_); }
private:
GMainContext* context_;
};
GlibBridge::GlibBridge(base::MessageLoopForIO* message_loop)
: glib_context_(g_main_context_default()),
message_loop_(message_loop),
state_(State::kPreparingIteration),
weak_ptr_factory_(this) {
message_loop_->task_runner()->PostTask(
FROM_HERE, base::Bind(&GlibBridge::PrepareIteration,
weak_ptr_factory_.GetWeakPtr()));
}
void GlibBridge::PrepareIteration() {
GMainContextLock _l(glib_context_);
g_main_context_prepare(glib_context_, &max_priority_);
int num_fds =
g_main_context_query(glib_context_, max_priority_, nullptr, nullptr, 0);
poll_fds_ = std::vector<GPollFD>(num_fds);
CHECK(watchers_.empty());
int timeout_ms;
g_main_context_query(glib_context_, max_priority_, &timeout_ms, &poll_fds_[0],
num_fds);
DVLOG(1) << "Preparing iteration with timeout " << timeout_ms << " ms, "
<< num_fds << " event FDs";
for (int i = 0; i < num_fds; i++) {
fd_map_[poll_fds_[i].fd].push_back(&poll_fds_[i]);
watchers_.emplace_back(
new base::MessageLoopForIO::FileDescriptorWatcher(FROM_HERE));
message_loop_->WatchFileDescriptor(poll_fds_[i].fd, true,
ConvertGPollFlags(poll_fds_[i].events),
watchers_.back().get(), this);
}
state_ = State::kWaitingForEvents;
if (timeout_ms < 0)
return;
base::TimeDelta timeout = base::TimeDelta::FromMilliseconds(timeout_ms);
timeout_closure_.Reset(
base::Bind(&GlibBridge::Dispatch, weak_ptr_factory_.GetWeakPtr()));
message_loop_->task_runner()->PostDelayedTask(
FROM_HERE, timeout_closure_.callback(), timeout);
}
void GlibBridge::OnEvent(int fd, int flag) {
for (GPollFD* poll_fd : fd_map_[fd])
poll_fd->revents |= flag;
if (state_ != State::kWaitingForEvents)
return;
message_loop_->task_runner()->PostTask(
FROM_HERE,
base::Bind(&GlibBridge::Dispatch, weak_ptr_factory_.GetWeakPtr()));
state_ = State::kReadyForDispatch;
}
void GlibBridge::Dispatch() {
GMainContextLock _l(glib_context_);
timeout_closure_.Cancel();
watchers_.clear();
if (g_main_context_check(glib_context_, max_priority_, poll_fds_.data(),
poll_fds_.size())) {
g_main_context_dispatch(glib_context_);
}
poll_fds_.clear();
max_priority_ = -1;
message_loop_->task_runner()->PostTask(
FROM_HERE, base::Bind(&GlibBridge::PrepareIteration,
weak_ptr_factory_.GetWeakPtr()));
}
void GlibBridge::OnFileCanWriteWithoutBlocking(int fd) {
OnEvent(fd, G_IO_OUT);
}
void GlibBridge::OnFileCanReadWithoutBlocking(int fd) {
OnEvent(fd, G_IO_IN);
}
} // namespace glib_bridge