WASI Library

WebAssembly System Interface (WASI) provides a modular and secure system interface designed to enable Wasm module to interact with the underlying operating system in a platform-independent manner.

MicroEJ provides its own WASI Library which implements a subset of WASI 0.1 APIs specification.

Moreover, some APIs have been extended to support certain POSIX functions that are not available in wasi-libc such as socket, bind, connect, etc.

Usage

To use the WASI Add-on Library, add the following to the project build file:

implementation("ej.library.runtime:wasi:0.2.0")

In your application code, call one of the WASI context initialization methods before calling any Managed C function:

  • Wasi.init(WasmModule) — initializes WASI context for the given Wasm module instance and pre-opens the root directory /.

  • Wasi.init(WasmModule,String[]) — initializes WASI context for the given Wasm module instance and pre-opens only the specified directories array. Pass null to pre-open none.

If a directory is not pre-opened, it cannot be accessed by the Wasm module.

Here is an example:

  • C source code (my_app.c):

    #include <stdlib.h>
    #include <stdio.h>
    #include <time.h>
    
    void print_time() {
        struct timespec ts;
        clock_gettime(CLOCK_REALTIME, &ts);
        printf("%lld.%.9ld\n", (long long)ts.tv_sec, ts.tv_nsec);
    }
    
  • Java source code (MyApp.java):

    package com.mycompany;
    
    import ej.lang.Alias;
    import ej.lang.WasmModule;
    import ej.wasi.Wasi;
    import java.io.IOException;
    
    @Alias("my_app")
    public final class MyApp extends WasmModule {
    
        public static void main(String[] args) throws IOException {
    
            MyApp myApp = new MyApp();
            Wasi.init(myApp);
    
            // Call the "print_time" Wasm function
            myApp.printTime();
        }
    
        @Alias("print_time")
        public native synchronized void printTime();
    
    }
    

In this example, the implementation of the POSIX-compliant C functions clock_gettime(...) and printf(...) relies on the WASI functions clock_time_get(...) and fd_write(...) which are provided by the WASI Add-on Library.

Note

WASI cannot be used by more than one Wasm module within a single Application. Otherwise, calls to WASI methods may result in undefined behavior. In particular, in Multi-Sandbox mode, WASI APIs must not be exposed as Kernel APIs. This restriction allows each Application and the Kernel to use WASI independently, while still limiting usage to a single Wasm module per context.

WASI APIs

List of the currently suppported WASI APIs:

Group

WASI APIs

FileSystem



path_open, fd_readdir, fd_close, fd_read, fd_pread, fd_write,
fd_pwrite, fd_seek, fd_fdstat_get, fd_fdstat_set_flags, fd_prestat_get,
fd_prestat_dir_name, fd_tell, fd_filestat_get, path_unlink_file,
path_remove_directory, path_filestat_get

Sockets

sock_accept, sock_shutdown, poll_oneoff, sock_recv, sock_send

Clocks

clock_time_get, clock_res_get

Process

proc_exit

High-Level WASI-Based POSIX APIs

List of the currently supported high-level WASI-based POSIX APIs. The pthread and semaphore entries are not exhaustive, and a * stands for the whole function family:

Group

High-Level WASI-Based POSIX APIs

FileSystem


open, fopen, fdopendir, close, fclose, read, write, fread,
fwrite, lseek, stat, fstat, readdir, fstatat, fileno, fflush,
access

Sockets

send, recv, shutdown, close

Clocks

clock_getres, clock_gettime, gettimeofday

Process

exit

StdIO

printf

Pthreads




Threads: pthread_create, pthread_join, pthread_detach, pthread_self.
Attributes: pthread_attr_*. Synchronization: pthread_mutex_*, pthread_cond_*,
pthread_rwlock_*, pthread_barrier_*, pthread_spin_*.
Thread-local storage: pthread_key_*, pthread_getspecific,
pthread_setspecific, pthread_once.
Semaphores

sem_init, sem_destroy, sem_wait, sem_timedwait, sem_trywait,
sem_post, sem_getvalue

High-Level Extended WASI POSIX APIs

List of the high-level extended WASI-based POSIX APIs that provide functionalities not available in the wasi-libc:

Group

High-Level Extended WASI POSIX APIs

Sockets

socket, bind, listen, connect

Miscs

dup, dup2, pipe

WASI Threads

MicroEJ provides support of POSIX threads (pthreads) in Managed C through the implementations of Threads Proposal for WebAssembly and WASI threads specification.

This allows running the following code:

#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>
#include <unistd.h>

void* thread_entry_point(void *ctx) {
   // Called in a new pthread
   return 0;
}

int main() {
   pthread_attr_t new_thread_attr;
   pthread_t new_thread;

   // Initialize the stack size
   pthread_attr_init(&new_thread_attr);
   int res = pthread_attr_setstacksize(&new_thread_attr, 2048);
   if(res != 0){
      // Unable to set the stack size
      return -1;
   }

   pthread_create(&new_thread, &new_thread_attr, &thread_entry_point, NULL);

   // ...

   pthread_join(new_thread, NULL);

   return 0;
}

Note

By default, if not specified, the WASI libc stack size is 128KB. It is strongly recommended to set the stack size at thread creation using the pthread_attr_setstacksize function.

When WASI threads are enabled, each new pthread created by Managed C code (via pthread_create) is backed by a new module instance that shares the linear memory of the parent instance. Each pthread instance has its own stack and TLS allocated within the shared linear memory, allowing threads to run independently while accessing the same data.

Enable WASI Threads

  • Compile and link your Wasm Module with these additional Clang options: --target=wasm32-wasi-threads -pthread.

  • Make your WasmModule subclass implement the WasiThreadModule interface and declare the wasi_thread_start method as an instance native synchronized method. Then call Wasi.init(WasmModule) or Wasi.init(WasmModule,String[]) before calling any Managed C function:

    package com.mycompany;
    
    import ej.lang.Alias;
    import ej.lang.WasmModule;
    import ej.wasi.Wasi;
    import ej.wasi.thread.WasiThreadModule;
    
    import java.io.IOException;
    
    @Alias("my_app")
    public final class MyApp extends WasmModule implements WasiThreadModule {
    
        public static void main(String[] args) {
    
            MyApp myApp = new MyApp();
            try {
                Wasi.init(myApp);
            } catch (IOException e) {
                // error during initialization
            }
    
            // ...
        }
    
        @Override
        public native synchronized void wasi_thread_start(int threadId, int args);
    }
    

Note

When the Wasm Module is not compiled with WASI Threads enabled, then WasmModule.clone() will throw a CloneNotSupportedException.