Integrate the Glib main loop into the libev event loop (C++)

294 views Asked by At

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?

0

There are 0 answers