MirageOS async support?

As far as I know, async cannot be used with MirageOS. Is there any work someone is doing to enable async to be used with a MirageOS unikernel?

1 Like

Hi,

Due to the fact that MirageOS is an operating system, usual syscalls and famous libc functions are surely not available in MirageOS essentially. Indeed, into details, MirageOS (for the Solo5 target) uses a special caml runtime: ocaml-freestanding which is an hacked version of OCaml with a static link to nolibc (and few others libraries).

This is why we say: if your library depends (transitively or not) to the Unix module, your project will not be (fully) available for MirageOS - and it’s why you can see some *-unix variants of various MirageOS libraries.

About async, or more precisely async_kernel and core_kernel, they widely use some syscalls which are not available in MirageOS (such as localtime for example). These projects are huge and it’s difficult for the MirageOS core team to try to remove properly these syscalls and maintain a smaller async compatible with MirageOS on our own way.

In the opposite, lwt did this split (between the scheduler and Unix syscalls) at the beginning. You can find lwt and lwt.unix. So, it was more easy to integrate lwt in MirageOS and the time decided to strictly use lwt as our scheduler - indeed, at some points, we abstracted the scheduler type 'a io but it was a bit a pain to maintain such abstraction (with a systematic specialisation to 'a Lwt.t) on all of our libraries.

So, of course, it should be possible to integrate async_kernel into MirageOS - tweak a bit with ocaml-freestanding to add some new syscalls or functorize async over some specific syscalls (such as Mirage_time.S or Mirage_clock.S) and let functoria about the application of these functors.

At the end, most of MirageOS projects are functorized over a scheduler. For example, you have a version of cohttp for async or some others libraries such as ocaml-tls or decompress don’t depends on any syscalls (so they can be used with async, lwt or … nothing!). This is on what we believe, be system-agnostic.

So about MirageOS itself, I don’t think that we have any plan to use async - as I said, it’s a huge work. But the limit is mostly on theasync side than MirageOS according to our abstraction - and I don’t blame Janestreet :slight_smile:, it’s fine and they may be don’t have the resource to do this job too! Indeed, we continuously think about lwt as a spontaneous dependency due to its design but we keep in our mind that the community is larger than lwt and the future will be about ocaml-multicore.

9 Likes

Great writeup, @dinosaure!

FWIW, the Async_unix/Async_kernel is meant to convey the distinction between the parts of Async that depend on Unix and those that don’t. But, “does not depend on Unix” in this case means “works in js_of_ocaml and theoretically on Windows” but not “works in MirageOS”, so for example Core_kernel's dependency on libc’s localtime (for use in strftime) is complemented by a separate JavaScript stub for strftime. This works fine for the first standard but not the second, sadly.

You are right :slight_smile: but evil are in the details!

Indeed, the problem comes with the Unix dependency but more globally, anything with C stubs (like async_kernel/core_kernel or the Unix module) will have a problem with MirageOS.

As I explained, the first problem is the non-availability of some C functions (due to nolibc). But the second problem is: how to compile a C stubs (like otherlibs/unix/*.c or your own C stubs). Indeed, C stubs must be compiled with specific *.h and, for Solo5, with specific flags.

MirageOS, or more specially ocaml-freestanding and Solo5 put some pkg-config files into your OPAM environment. By this way, some libraries such as digestif or mirage-crypto can compile C stubs with specific flags. Currently, these flags are available with:

PKG_CONFIG_PATH := $(opam config var prefix)/lib/pkgconfig
PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) pkg-config --cflags ocaml-freestanding

You can see a real example with mirage-crypto here.

Then, the mirage tool does the right things about the link-step (to craft the operating system). Indeed, it will take these special artifact with ocamlfind (unusable for a simple executable) with your operating system. By this way, we ensure a sanitized ELF object (between nolibc, ocaml-freestanding - the caml runtime and third-part libraries with C stubs such as mirage-crypto or digestif).

Such plumbing is a bit a pain and error-prone. So, in the case of async_kernel/core_kernel, the second problem will be to ask JaneStreet to compile twice the *.a of C stubs:

  • one for an usual link with a simple host OCaml program
  • the other to be able to link them with our special caml-runtime (ocaml-freestanding)

This is what we did for bigstringaf which is a library outside the scope of the MirageOS eco-system but widely used by us. But, about async, the problem will be more complex - the codebase is bigger and we talk about C :slight_smile: so we can have some surprises!

Of course, at this point, you can say that such solution does not really scale and… you are true. Even if we want to limit as much as possible C stubs in our codebase, we reached several times some limitations (cross-compilation comes to my mind). This is an other big difference with lwt, the core library does not have any C stubs!

However, the good news is (some teasing…): MirageOS 4! With the MirageOS core team, we currently work on a realistic solution about C stubs. I don’t want to go to details but the next release is through to fix these problem specially (with dune).

3 Likes

So the first biggest issue (roadblock) with attempting to adapt Async to MirageOS is how the c stubs are handled by the MirageOS build system. After that the async stubs will need to be adapted to MirageOS?

So MirageOS 4 - with its dune integration - aims to solve the first issue?

Not exactly unfortunately, the first problem (about unavailable C functions) remains. However, MirageOS 4 should help to extend the set of MirageOS targets. Due to the story of ocaml-freestandng/Solo5/Xen, we are currently stuck to few targets (Unix, Solo5 & Xen) and an extension of them need an incredible work on third-part libraries (digestif and mirage-crypto will must compile a third artifact according to the new target).

MirageOS 4 wants to solve that and propose an other way to link third-part libraries - more concretely, we will compile locally third-part libraries according to your requested target (and C flags) and solve by this way the question about C stubs.

The solution focus on:

  • the extension of targets (including cross-compiled targets)
  • third-part libraries (with C stubs)

As you can see, we don’t talk about scheduler here because several questions comes:

  • the type 'a io currently specialized to 'a Lwt.t. Even if most of our libraries abstract this type, it’s not systematically the true. For instance, ocaml-git and Irmin uses lwt directly
  • the main loop, as you can see here is defined by the back-end chosen (where mirage-unix use lwt.unix/Lwt_engine)

So a move to async (with MirageOS 4 - which is not released yet :stuck_out_tongue: ) implies:

  • some patches on ocaml-freestanding (to implement missing C functions)
  • a new mirage back-end (with another implementation of the main loop)
  • an systemic abstraction (with functors) of some famous MirageOS libraries

If you look into details, most of issues is about a inherent technical debt about our choices in the past. Anything is possible :slight_smile: but should we spend our time to that? As I said most of times, we are few to maintain MirageOS and we must choose intelligently our path. For my perspective, I never used async and I don’t need it when lwt exists on the other side (and fits well in our case) - but I’m surely biased when I did my first internship at ocsigen :smiley: !

Of course, if anyone wants async with MirageOS, the path is open - and we will gladly help you.

2 Likes

Thank you for the detailed answer @dinosaure. Much appreciated.