Modules

Io

moduleSource
macOSLinuxWindows

#import "Extensions/Io";

Fibers and a cooperative scheduler. A jaic extension, not official Jai: other Jai compilers have no such module, and it may change between jaic releases. See docs/extensions/io-engine.md.

Example
Io :: #import "Extensions/Io";

results: *Io.Channel(int); // a fiber's procedure cannot capture locals: share through a global or pass data

main :: () {
    Io.io_run(() { // the scheduler runs on this thread
        results = Io.make_channel(int, 4);
        defer Io.free_channel(results);

        for 1..4 Io.spawn((n: int) { Io.sleep(10 * n); Io.send(results, n); }, it);

        total := 0;

        for 1..4 {
            value, err := Io.recv(results);
            assert(err == .NONE);
            total += value;
        }

        assert(total == 10);
    });
}

Doesn't run in the browser (skipped on wasm).

Show moreShow less

A fiber is a lightweight thread that the program switches itself: it has its own small stack, and runs until it blocks (sleep, yield, a channel, a Wait_Group, a Fiber_Mutex), when the scheduler runs another one. All fibers of an io_run share one OS thread, so they never run at the same time and need no locks to share data. A blocking call returns .CANCELED once cancel(fiber) has been called for its fiber.

Fibers need the compiler's stack-switching intrinsics, which native builds and the interpreter (jaic -interp) have and WebAssembly does not: importing this module for wasm is a compile error. The stack of each fiber, in bytes, including its guard page. Address space only: the OS commits pages as the fiber touches them (Windows commits it all up front).

Procedures#

Scheduler#

io_run2 overloads
(proc: $P)

Runs proc as the first fiber of a new scheduler on this thread and returns when it has returned. Fibers it started that are still alive then are canceled, and run until they end.

Io :: #import "Extensions/Io";

done: bool;

main :: () {
    Io.io_run(() {
        Io.sleep(1);
        done = true;
    });

    assert(done);
}

Doesn't run in the browser (skipped on wasm).

spawn2 overloads
(proc: $P) -> Fiber_Handle

Starts proc as a new fiber on the current scheduler; it first runs when the caller blocks or yields. It gets a copy of the caller's context (with temporary storage of its own) and data is copied for it.

Io :: #import "Extensions/Io";

main :: () {
    Io.io_run(() {
        results := Io.make_channel(int, 1);
        defer Io.free_channel(results);

        Io.spawn((out: *Io.Channel(int)) { Io.send(out, 42); }, results);

        value, error := Io.recv(results); // waits for the spawned fiber
        assert(error == .NONE && value == 42);
    });
}

Doesn't run in the browser (skipped on wasm).

() -> Io_Error

Lets the other ready fibers run first. .CANCELED if this fiber was canceled.

(milliseconds: s64) -> Io_Error

Blocks this fiber, not the thread, for at least milliseconds. Ends early with .TIMED_OUT when a with_deadline limit comes first.

(handle: Fiber_Handle)

Makes the blocking calls of fiber return .CANCELED, now and from now on. The call it is blocked in, if any, returns at once. Does nothing for a fiber that has ended.

() -> bool

Whether the running fiber was canceled, for loops that do not block.

() -> bool

Whether the calling code runs on a fiber of a scheduler, that is inside io_run.

()

Gives the memory of the running fiber's stack that no frame uses any more back to the system: the pages below the current frames that earlier, deeper calls touched. Call it before a long wait (an idle connection) so those pages do not stay resident; it costs a system call, so not in a hot path. The stack's contents below the caller are lost, which nothing can rely on anyway. Does nothing outside a fiber or on a platform without a way to discard pages.

Sync#

($T: Type, capacity := 0) -> *Channel(T)

A channel that holds up to capacity values, made with context.allocator.

(channel: *Channel($T))

Frees a channel from make_channel. No fiber may be waiting on it.

(channel: *Channel($T), value: T) -> Io_Error

Puts value into the channel, blocking while it is full. .CLOSED if the channel is (or becomes) closed, .CANCELED if the fiber is canceled while it waits.

(channel: *Channel($T), value: T) -> bool

A send that gives up instead of blocking: whether the value was taken.

(channel: *Channel($T)) -> (T, Io_Error)

Takes the oldest value, blocking while the channel is empty. .CLOSED once it is closed and drained, .CANCELED if the fiber is canceled while it waits.

(channel: *Channel($T)) -> (T, bool)

A recv that gives up instead of blocking: the value and whether there was one.

close7 overloads
(channel: *Channel($T))

No more values will be sent. Fibers waiting to send get .CLOSED; receivers get what is buffered, then .CLOSED.

notify_onesince 0.27.0
(condition: *Condition)

Wakes the fiber that has waited longest, if any.

Net#

(a: u8, b: u8, c: u8, d: u8, port: u16) -> Address

An IPv4 address from its four octets, a.b.c.d, and a port in host order. address_to_string turns it back into text.

address := address_v4(127, 0, 0, 1, 8080);

assert(address.family == .IPV4);
assert(address.bytes[0] == 127 && address.bytes[3] == 1);
assert(address_to_string(address) == "127.0.0.1:8080");

Doesn't run in the browser (skipped on wasm).

(text: string) -> (Address, bool)

Reads an address literal: 1.2.3.4:80, :8080 (any IPv4 address), [::1]:80, 1.2.3.4, ::1. It does not look names up; resolve does.

() -> s32

The system's error number behind the last failed IO call of the running fiber.

tcp_listen2 overloads
(text: string, backlog := 1024, reuse_port := false) -> (Tcp_Listener, Io_Error)

Listens on address (a literal such as ":8080", port 0 picks a free one; see local_address). reuse_port lets several listeners, one per thread, share the port; the kernel then spreads the connections between them.

Io :: #import "Extensions/Io";

main :: () {
    Io.io_run(() {
        listener, error := Io.tcp_listen("127.0.0.1:0");
        assert(error == .NONE);
        defer Io.close(listener);

        address, _ := Io.local_address(listener);
        assert(address.port != 0); // the system picked a free port

        _, bad := Io.tcp_listen("not an address");
        assert(bad == .INVALID);
    });
}

Doesn't run in the browser (skipped on wasm).

unix_listensince 0.27.0
(path: string, backlog := 128, replace := false) -> (Tcp_Listener, Io_Error)

Listens on the socket file path. The file must not exist yet; with replace an existing file at path is removed first (whatever it is, so use it only for a path you own). The socket file stays after close: remove it when the server is done.

local_address2 overloads
(listener: Tcp_Listener) -> (Address, Io_Error)

The address this socket is bound to; with tcp_listen(":0"), which port it got.

Io :: #import "Extensions/Io";

main :: () {
    Io.io_run(() {
        listener, _ := Io.tcp_listen("127.0.0.1:0");
        defer Io.close(listener);

        address, error := Io.local_address(listener);
        assert(error == .NONE);
        assert(address.family == .IPV4 && address.port != 0);
    });
}

Doesn't run in the browser (skipped on wasm).

(stream: Tcp_Stream) -> (Address, Io_Error)

The far end of a connected stream. On the accepting side this is the client's address and its ephemeral port; on the connecting side it is the server's.

Io :: #import "Extensions/Io";

main :: () {
    Io.io_run(() {
        listener, _ := Io.tcp_listen("127.0.0.1:0");
        defer Io.close(listener);

        address, _ := Io.local_address(listener);
        client, connect_error := Io.connect(address);
        assert(connect_error == .NONE);
        defer Io.close(client);

        server, accept_error := Io.accept(listener);
        assert(accept_error == .NONE);
        defer Io.close(server);

        far, _ := Io.peer_address(client);
        assert(far.port == address.port); // the client's peer is the listener
        near, _ := Io.local_address(client);
        seen, _ := Io.peer_address(server);
        assert(seen.port == near.port); // the server sees the client's own port
    });
}

Doesn't run in the browser (skipped on wasm).

read4 overloads
(stream: Tcp_Stream, buffer: [] u8) -> (s64, Io_Error)

Waits for data and reads what is there, up to buffer.count bytes. 0 means the peer closed its end. An error comes with 0 bytes.

wait_readable2 overloads
(stream: Tcp_Stream) -> Io_Error

Waits until read would not wait: bytes have arrived, the peer closed its end, or the connection failed (read then says which). Nothing is consumed and no buffer is needed, so a fiber can wait for a quiet connection and take its memory only when there is something to read. Ends with .TIMED_OUT or .CANCELED like any other wait, and .CLOSED if the socket is closed meanwhile.

read_full4 overloads
(stream: Tcp_Stream, buffer: [] u8) -> (s64, Io_Error)

Reads until buffer is full. .END_OF_FILE if the peer closed first, with the bytes read.

write_some4 overloads
(stream: Tcp_Stream, data: [] u8) -> (s64, Io_Error)

Sends some of data: at least one byte unless data is empty. Returns how many.

write8 overloads
(stream: Tcp_Stream, data: [] u8) -> (s64, Io_Error)

Sends all of data, waiting as often as it takes. On an error it returns how many bytes went out.

(host: string, port: u16) -> (Address, Io_Error)

Looks host up (an address literal, or a name for the system's resolver, which runs on a helper thread so only this fiber waits) and gives its first address, IPv4 preferred.

Pipe#

Two connected ends. What one writes the other reads. close both when done (they free themselves once both are closed).

Files#

(file: *Io_File, buffer: [] u8, offset: s64) -> (s64, Io_Error)

Reads at offset; the file's position does not move.

write_at2 overloads
(file: *Io_File, data: [] u8, offset: s64) -> (s64, Io_Error)

Writes all of data at offset; the file's position does not move.

(path: string) -> ([] u8, Io_Error)

The whole content of a file, in memory from context.allocator (free it with free).

write_file2 overloads
(path: string, data: [] u8) -> Io_Error

Creates or replaces a file with data.

Stream#

(reader: Reader) -> bool

Whether wait_readable works on reader, which is when its source supplies a wait_proc (a TCP stream does; a file or a pipe end does not). When it is false, wait_readable returns .UNSUPPORTED.

Io :: #import "Extensions/Io";

main :: () {
    Io.io_run(() {
        near, far := Io.make_pipe();
        defer Io.close(near);
        defer Io.close(far);

        assert(!Io.can_wait_readable(Io.reader_of(near)));
    });
}

Doesn't run in the browser (skipped on wasm).

(reader: Reader, size_hint := 0) -> ([] u8, Io_Error)

Reads to the end of the source into memory from context.allocator (free it with free). size_hint is the expected length, when it is known. On any error the bytes read so far are freed.

reader_of3 overloads
(file: *Io_File) -> Reader

The file as a Reader. Reading advances the file's position. The reader points at the Io_File, which must outlive it.

writer_of3 overloads
(file: *Io_File) -> Writer

The file as a Writer. Writing advances the file's position. The writer points at the Io_File, which must outlive it.

JSON#

(reader: Reader, $T: Type, options := Json.Json_Options.{}) -> (T, Json.Json_Error)

Reads a document to the end of reader and decodes it as a T: the same value or error as from_json(T, text) for the same bytes, however they arrive (a pipe written in small pieces, a socket). The pieces are gathered in one buffer that is freed before this returns. A reader that fails gives a .READ error naming the Io_Error; a document over options.max_size is a .LIMIT error and the rest of the stream is left unread. The reader is not closed.

Runtime#

(milliseconds: s64, body: Code)

The one way to bound a wait. Limits every IO call (and sleep) in body to milliseconds from now: one that is still waiting then returns .TIMED_OUT, and so does a call made after the limit, even when data is ready. 0 is a limit that has already passed. Deadlines nest; the earlier one wins, and the previous deadline is back when body ends. A negative milliseconds adds no limit (NO_DEADLINE), so a timeout setting that may be off needs no second code path. Channel and lock operations are not IO and ignore it.

Io :: #import "Extensions/Io";

main :: () {
    Io.io_run(() {
        client, server := Io.make_pipe();
        buffer: [8] u8;
        error: Io.Io_Error;

        Io.with_deadline(20, #code {
            _, error = Io.read(client, buffer);       // nothing is ever sent
        });

        assert(error == .TIMED_OUT);
    });
}

Doesn't run in the browser (skipped on wasm).

with_gracesince 0.27.0
(milliseconds: s64, body: Code)

Runs body clear of the running fiber's deadline and cancel, for cleanup that has to happen after one of them fired: telling a server to stop a query, a goodbye message. body has its own limit of milliseconds (negative: none), and with_deadline inside it nests as usual. When body ends, the outer deadline is back, and the fiber is canceled if it was before or a cancel came during body.

Io :: #import "Extensions/Io";

main :: () {
    Io.io_run(() {
        client, server := Io.make_pipe();
        buffer: [8] u8;
        error: Io.Io_Error;
        sent: s64;

        Io.with_deadline(0, #code {
            _, error = Io.read(client, buffer);                // .TIMED_OUT: the deadline has passed
            Io.with_grace(1000, #code { sent, _ = Io.write(client, "stop"); });
        });

        assert(error == .TIMED_OUT);
        assert(sent == 4);
    });
}

Doesn't run in the browser (skipped on wasm).

(backend: Backend) -> string

The name of a backend as JAI_IO_BACKEND spells it, such as "epoll", or "none" for .NONE. The string is a constant.

assert(name_of_backend(.EPOLL) == "epoll");
assert(name_of_backend(.NONE) == "none");

Doesn't run in the browser (skipped on wasm).

() -> Backend

The backend the running scheduler uses: .IO_URING, .EPOLL, .KQUEUE, .IOCP, .IORING, or .NONE where IO is not implemented. Set JAI_IO_BACKEND to io_uring, epoll, kqueue, iocp or ioring before io_run to ask for one; a backend the system cannot offer is replaced (see backend_reason).

() -> string

backend() as text: "io_uring", "epoll", "kqueue", "iocp", "ioring" or "none".

() -> string

Why the running scheduler does not use the backend that was asked for or is the first choice on this system (for example "io_uring is disabled here"). Empty when it does.

() -> s64

The number of processors, for sizing io_run_threads.

io_run_threads2 overloads
(count: s64, proc: $P)

Starts count schedulers, each on a thread of its own, runs proc as the first fiber of every one, and returns when all have finished. Fibers never move between them. With reuse_port listeners (tcp_listen(..., reuse_port = true)) each thread accepts its own connections.

Io :: #import "Extensions/Io";
Atomics :: #import "Atomics";

started: s32;

main :: () {
    Io.io_run_threads(2, () {
        Atomics.atomic_add(*started, 1);
    });

    assert(started == 2);
}

Doesn't run in the browser (skipped on wasm).

(key: *void) -> *void

The value a library stored for key on the running scheduler, or null. Keys are addresses of something the library owns (a global works). This is the per-thread variable the language does not have: the Http client keeps its connection pool here.

(key: *void, value: *void, drop: Local_Drop)

Stores value for key on the running scheduler; drop (if not null) gets it when the scheduler ends. Replaces an earlier value for the key without dropping it.

Interrupt#

()

Makes SIGINT and SIGTERM set the flag interrupted reads, instead of ending the process. The handler is installed once for the whole program; calling this again does nothing.

() -> bool

True once SIGINT or SIGTERM arrived after catch_interrupts, or request_interrupt ran.

()

Sets the flag by hand, as a signal would (tests and programs that stop themselves).

()

Clears the flag (tests that run several servers in one process).

Overflow#

(signal: s32, info: *POSIX.siginfo_t, secret: *void)

Engine uring#

uring_start2 overloads
(engine: *Engine) -> string

Tries to make the ring; returns why it could not, or "" if engine now uses io_uring.

(scheduler: *Scheduler, engine: *Engine) -> *Uring.io_uring_sqe

A free submission entry. With none free, the queued ones go to the kernel first, and completions are reaped so that a full completion queue cannot hold submission back.

uring_submit2 overloads
(scheduler: *Scheduler, engine: *Engine, op: *Op)
(scheduler: *Scheduler, engine: *Engine, op: *Op)

Operations still waiting on a descriptor would keep the file alive past the close, so they are canceled first. The cancel is hard-linked so that finding nothing to cancel does not stop the close.

uring_cancel2 overloads
(scheduler: *Scheduler, engine: *Engine, op: *Op)
uring_wait2 overloads
(scheduler: *Scheduler, engine: *Engine, timeout: s64)
(scheduler: *Scheduler, engine: *Engine)
(handle: Handle) -> (bool, s64, Io_Error)

accept through one multishot request per listener instead of one request per connection. Returns false (and does nothing) when this call has to use a one-shot accept instead: another fiber is already waiting on the listener, or the request is being canceled.

(scheduler: *Scheduler, op: *Op, result: s64, flags: u32)
(scheduler: *Scheduler, engine: *Engine, fd: s64)

The listener fd is being closed: sockets accepted ahead of time are closed, and the slot goes when the kernel has ended its request (the close cancels it).

(scheduler: *Scheduler, engine: *Engine, fd: s64)

close(now = true): the descriptor is closed without waiting, so the multishot request has to be canceled by hand or it would keep the socket open.

Engine IOCP#

iocp_post2 overloads
(port: s64, key: u64)

Wakes the scheduler that owns port from any thread.

iocp_failWindows
(op: *Op, error: s64) -> bool

Finishes op at once with the Win32 error error (negated into the result).

(op: *Op, succeeded: bool, error: s64) -> bool

After an overlapped call: the packet will come if it succeeded or is pending.

iocp_submit2 overloads
(scheduler: *Scheduler, engine: *Engine, op: *Op) -> bool

Starts op. True: it finished now (the result is set). False: its packet will arrive.

iocp_fileWindows
(op: *Op) -> bool

The file offset goes into the OVERLAPPED. A negative one means the end of the file (for appending handles, which only have FILE_APPEND_DATA access) or the start.

iocp_cancel2 overloads
(scheduler: *Scheduler, engine: *Engine, op: *Op)
iocp_wait2 overloads
(scheduler: *Scheduler, engine: *Engine, timeout: s64)

Takes packets for up to timeout nanoseconds (forever if negative), finishing operations.

(scheduler: *Scheduler, engine: *Engine, op: *Op, status: s32, bytes: u32)

The packet of op arrived with this NTSTATUS and byte count.

Engine ioring#

ring_probeWindows
() -> string

Whether this system has a usable IoRing: "" if so, else why not. The answer is kept.

(api: Ring_Api, ring: s64, event: s64) -> bool

Reads the first two bytes of the running executable through ring and waits for the event, then checks that the ring accepts a write entry.

ring_openWindows
(state: *Iocp_State) -> string

Makes this engine's ring. Returns why it cannot, or "" if files now use it.

(thread: *Thread.Thread) -> s64

Waits for the ring's completion event and tells the scheduler's port.

(scheduler: *Scheduler, engine: *Engine, op: *Op) -> bool

A file read or write in the ring. True: it failed at once. False: its completion will come.

(state: *Iocp_State, op: *Op) -> bool

Puts op in the submission queue. False if the ring refused it.

ring_errorWindows
(result: s32) -> s64

The Win32 error an HRESULT from the ring stands for (0 for success).

ring_drainWindows
(scheduler: *Scheduler, engine: *Engine)

Takes the completions that are in the ring, finishes their operations, and lets waiting ones in.

(scheduler: *Scheduler, engine: *Engine)

Blocks the thread until every operation in the ring has finished, for close of a file.

Types#

:: enum

Why a blocking call returned early or failed. platform_code() gives the system's own error number for the last failure of the running fiber, which is how OTHER stays useful.

:: enum

Where a fiber is in its life: waiting in the pool, ready, running, blocked on a timer, channel or mutex, or finished. It is the scheduler's own bookkeeping; programs rarely need it.

Fiberstruct
:: struct

One fiber. Treat it as opaque: use Fiber_Handle to refer to one from outside the scheduler.

:: struct

A name for one fiber, returned by spawn. It stays safe to use after the fiber has ended: the generation tells a reused fiber slot from the one the handle named, so cancel and join on a stale handle do nothing.

Channelstruct
:: struct

A bounded queue of T between fibers. send blocks while it is full, recv while it is empty. With capacity 0 a send and a receive meet: each waits for the other.

:: struct

Waits for a number of tasks, as many as were added and have not yet been done.

:: struct

A lock for fibers. A fiber that cannot take it waits without blocking the thread, so unlike Thread's Mutex it is safe to hold across a sleep or a channel operation. It is not recursive, and waiters get it in the order they asked.

Conditionstructsince 0.27.0
:: struct

Fibers waiting until another fiber says that something changed. Unlike channels and locks, wait honours with_deadline. A condition carries no value and remembers nothing: a notify with nobody waiting is lost, so wait in a loop that checks the state itself.

Io :: #import "Extensions/Io";

changed: Io.Condition;
ready: bool;

main :: () {
    Io.io_run(() {
        error: Io.Io_Error;

        // Nothing changes `ready` here, so the wait ends at the deadline.
        Io.with_deadline(20, #code {
            while !ready {
                error = Io.wait(*changed);           // .TIMED_OUT, .CANCELED
                if error != .NONE break;
            }
        });

        assert(error == .TIMED_OUT);
    });
}

Doesn't run in the browser (skipped on wasm).

:: enum

The IP version of an Address: IPV4, IPV6, or NONE for an Address that was never filled in (and for what parse_address returns when the text is not an address).

address, ok := parse_address("[::1]:8080");
assert(ok && address.family == .IPV6 && address.port == 8080);

address, ok = parse_address("127.0.0.1:80");
assert(ok && address.family == .IPV4);

address, ok = parse_address("not an address");
assert(!ok && address.family == .NONE);

Doesn't run in the browser (skipped on wasm).

Addressstruct
:: struct

An IP address and port. bytes is in network order; an IPv4 address uses the first four.

:: struct

A connected socket, from accept or connect.

Pipe_Halfstruct
:: struct

One direction of a pipe.

:: struct

One end of a pipe made by make_pipe.

Io_Filestruct
:: struct

An open file. read and write use and advance position; read_at and write_at do not. It has a position, so every procedure takes it by pointer.

:: #type (data: *void, buffer: [] u8) -> s64, Io_Error

Reads what is there, up to buffer.count bytes, waiting for at least one: 0 means the end.

:: #type (data: *void, bytes: [] u8) -> s64, Io_Error

Writes at least one byte of bytes (all of it for a sink that never waits) and returns how many.

:: #type (data: *void) -> Io_Error

Waits until read would not have to wait: a byte is there, the source ended, or it failed (the read that follows says which). Nothing is consumed, so it needs no buffer.

:: #type (data: *void, now: bool) -> Io_Error

Closes the source. now asks for a close that does not wait, for code that cannot block.

Readerstruct
:: struct

A source of bytes: a pointer to its data and the procedures to use on it. reader_of makes one for a file, a TCP stream or a pipe end, and a struct of your own can fill in read_proc (and optionally close_proc and wait_proc). A Reader is a handle: copy it freely, it owns nothing, and the source must stay alive while it is used. close on it closes the source.

Counter :: struct {
    next: u8;
}

count_up :: (data: *void, buffer: [] u8) -> s64, Io_Error {
    counter := cast(*Counter) data;

    for 0..buffer.count - 1 {
        buffer[it] = counter.next;
        counter.next += 1;
    }

    return buffer.count, .NONE;
}

main :: () {
    counter: Counter;
    source := Reader.{ data = *counter, read_proc = count_up };

    bytes: [4] u8;
    count, error := read(source, bytes);

    assert(error == .NONE && count == 4);
    assert(bytes[0] == 0 && bytes[3] == 3);
}

Doesn't run in the browser (skipped on wasm).

Writerstruct
:: struct

A sink for bytes, the counterpart of Reader: a pointer to its data, a write_proc and an optional close_proc. writer_of makes one for a file, a TCP stream or a pipe end. It is a handle that owns nothing; close on it closes the sink.

:: enum

The system facility a scheduler waits on: IO_URING or EPOLL on Linux, KQUEUE on macOS, IOCP or IORING on Windows. The scheduler picks the best one the machine offers; the JAI_IO_BACKEND environment variable names another (io_uring, epoll, kqueue, iocp, ioring). NONE is a system with no IO, where every operation fails with .UNSUPPORTED. Behaviour seen through the API is the same on all of them; they differ in speed and in the kernel versions they need.

:: struct

What the engine found it could use. Backends fill in what applies.

:: #type (value: *void)

What set_scheduler_local is given to release a value when its scheduler ends. It runs on the scheduler's thread after every fiber has ended, so it must not block: close sockets with close(stream, now = true).

:: struct

One listening socket's multishot accept. A fiber in accept waits on it; connections that arrive while nobody waits are queued for the next call.

:: struct

The answer a parked accept is waiting for; lives on that fiber's stack.

:: struct

What the Windows engine keeps. The fields exist everywhere so that Engine has one shape.

Io_Ring_RefstructWindows
:: struct

IORING_HANDLE_REF and IORING_BUFFER_REF: a kind (0: a raw handle or address) and the value.

Ring_CreatetypeWindows
:: #type (version: u32, flags: Io_Ring_Flags, submission: u32, completion: u32, ring: *s64) -> s32 #c_call
Ring_ClosetypeWindows
:: #type (ring: s64) -> s32 #c_call
Ring_QuerytypeWindows
:: #type (capabilities: *Io_Ring_Capabilities) -> s32 #c_call
Ring_SubmittypeWindows
:: #type (ring: s64, wait_count: u32, milliseconds: u32, submitted: *u32) -> s32 #c_call
Ring_PoptypeWindows
:: #type (ring: s64, cqe: *Io_Ring_Cqe) -> s32 #c_call
Ring_Set_EventtypeWindows
:: #type (ring: s64, event: s64) -> s32 #c_call
Ring_ReadtypeWindows
:: #type (ring: s64, file: Io_Ring_Ref, buffer: Io_Ring_Ref, count: u32, offset: u64, user_data: u64, flags: u32) -> s32 #c_call
Ring_WritetypeWindows
:: #type (ring: s64, file: Io_Ring_Ref, buffer: Io_Ring_Ref, count: u32, offset: u64, write_flags: u32, user_data: u64, flags: u32) -> s32 #c_call
Ring_ApistructWindows
:: struct

The addresses of the functions, found once per process.

Constants#

:: -1

A with_deadline argument that adds no limit.

:: 512

Submission queue size. The completion queue is twice that, and overflowing completions are kept by the kernel until reaped.

:: 1

user_data of entries nobody waits for.

:: 0x80000

Accepted sockets block: io_uring waits for them itself.

:: 128

Accepted connections wait here when no fiber is in accept; more than this and the multishot request is canceled until the queue drains.

ENTRIESWindows
:: 64

Completion packets dequeued per call.

:: 300

The ring versions to try, newest first: IORING_VERSION_3 is 300 in ioringapi.h. File writes need one of the later versions; the self test builds a write entry to find out which a system accepts.