Event loops

Event loops

This is the paradigm at the core of JavaScript, where there is no thread but only an event loop. This also very heavily used in UI, because you do actions only when some events happen like a click on a button.

Events loops like the ones we are going to see often are abstractions on top of pollers like we saw in the previous chapter. They tend to be multi-platform thus using either epoll, kqueue or any other depending on the host system.

libxev

Libxev is a Zig library that provides an event loop for non-blocking IO. The project state is "alpha-ish" to cite them from their README.

The mains reasons behind the creation of this library 2 years ago were: using io_uring under the hood, and writing an event loop like libuv but in Zig.

The first reason is not really relevant anymore, because since then libuv has implemented io_uring aswell so that really is not a plus anymore.

For the second reason while being commendable and nice to use since it is native Zig, is not really a massive argument for someone who just want to have an event loop, because libuv is already a very good library that is used by a lot of people and is very stable. The only problem of libuv is that it is written in C, so you might have to do a bit of gymnastics in order to use it.

libuv

In order to use libuv, which a C library in your project we are going to have to link the libc and add the -uv compilation option in the build.zig file like so:

  exe.linkLibC();
  exe.linkSystemLibrary("uv");

After that we are going to reproduce the basic exemple in C provided by the libuv documentation in Zig, so here is the C code:

  #include <stdio.h>
  #include <uv.h>
  
  int main() {
      uv_loop_t *loop = uv_default_loop();
  
      printf("Default loop.\n");
      uv_run(loop, UV_RUN_DEFAULT);
  
      uv_loop_close(loop);
      return 0;
  }

And the equivalent Zig code:

  const std = @import("std");
  const c = @cImport({
      @cInclude("stdio.h");
      @cInclude("uv.h");
  });
  
  pub fn main() !void {
      const loop: [*c]c.uv_loop_t = c.uv_default_loop();
  
      std.debug.print("Default loop.\n", .{});
      _ = c.uv_run(loop, c.UV_RUN_DEFAULT);
  
      _ = c.uv_loop_close(loop);
  }

Conclusion

Both use the same technology under the hood, being poller like kqueue, epoll, etc. So there is no big differences between the two in term of semantics.

As we can see with those exemples, it is so easy to work with a C library, so using libxev truly is not a good solution, since we can use libuv seemlessely. Moreover, libuv is a very stable library that is used by a lot of people and is very well maintained.

On the contrary libxev does not have frequent updates, which makes it hard to use in a constantly moving Zig environment. By the time of the writing of this, there is no 0.12.0 version of libxev available yet. The PR has been made days ago but it is still not merged. Even though 0.12.0 has been out for more than a month now.