I am trying to integrate the Glib
main loop into the libev
event loop. Actually, I am using their C++ wrappers: glibmm
[1] and ev++
[2]. The main idea was taken from the EV::Glib
Perl module [3]. However, my implementation sometimes hangs when I try to perform some async task (e.g., reading a text file from the filesystem):
#include <ev++.h>
#include <giomm.h>
#include <glibmm.h>
struct context;
int to_ev_events(Glib::IOCondition events) {
int result = EV_NONE;
if ((events & Glib::IOCondition::IO_IN) != Glib::IOCondition{})
result |= EV_READ;
if ((events & Glib::IOCondition::IO_OUT) != Glib::IOCondition{})
result |= EV_WRITE;
return result;
}
Glib::IOCondition to_glib_events(int events) {
Glib::IOCondition result{};
if ((events & EV_READ) != EV_NONE) result |= Glib::IOCondition::IO_IN;
if ((events & EV_WRITE) != EV_NONE) result |= Glib::IOCondition::IO_OUT;
return result;
}
struct prepare_watcher {
prepare_watcher(context &c) : ctx{c} {
watcher.priority = EV_MINPRI;
watcher.set(this);
watcher.start();
}
void operator()(ev::prepare &watcher, int revents);
ev::prepare watcher;
context &ctx;
};
struct check_watcher {
check_watcher(context &c) : ctx{c} {
watcher.priority = EV_MAXPRI;
watcher.set(this);
watcher.start();
}
void operator()(ev::check &watcher, int revents);
ev::check watcher;
context &ctx;
};
struct timer_watcher {
timer_watcher() { watcher.priority = EV_MINPRI; }
ev::timer watcher;
};
struct io_watcher {
io_watcher(int fd, int events) {
watcher.priority = EV_MINPRI;
watcher.start(fd, events);
}
ev::io watcher;
};
struct context {
Glib::RefPtr<Glib::MainContext> context = Glib::MainContext::get_default();
ev::default_loop loop;
std::vector<Glib::PollFD> poll_fds;
int priority{};
prepare_watcher prepare{*this};
check_watcher check{*this};
timer_watcher timer;
std::map<int, io_watcher> ios;
};
void prepare_watcher::operator()(ev::prepare &watcher, int revents) {
ctx.context->dispatch();
ctx.context->prepare(ctx.priority);
ctx.poll_fds.clear();
int timeout = 0;
ctx.context->query(ctx.priority, timeout, ctx.poll_fds);
for (Glib::PollFD &poll_fd : ctx.poll_fds) {
int fd = poll_fd.get_fd();
ctx.ios.try_emplace(fd, fd, to_ev_events(poll_fd.get_events()));
}
if (timeout >= 0) ctx.timer.watcher.start(timeout * 1e-3);
}
void check_watcher::operator()(ev::check &watcher, int revents) {
for (Glib::PollFD &poll_fd : ctx.poll_fds) {
int fd = poll_fd.get_fd();
io_watcher &io = ctx.ios.at(fd);
if (io.watcher.is_pending())
poll_fd.set_revents(
to_glib_events(ev_clear_pending(ctx.loop.raw_loop, &io.watcher)));
ctx.ios.erase(fd);
}
if (ctx.timer.watcher.is_active() || ctx.timer.watcher.is_pending())
ctx.timer.watcher.stop();
ctx.context->check(ctx.priority, ctx.poll_fds);
}
int main() {
Gio::init();
context ctx;
Glib::RefPtr<Gio::File> file = Gio::File::create_for_path("file.txt");
file->read_async([&](Glib::RefPtr<Gio::AsyncResult> result) {
file->read_finish(result);
ctx.loop.break_loop(ev::ALL);
});
ctx.loop.run();
}
Any ideas?