Landin library reference source

core/fault

Shared: available on every enabled target.

Deterministic allocation-failure injection over any allocator, with cumulative counters.

new wraps a provider in an injector. The budget counts delegated attempts, including attempts the inner provider refuses, so wrapping an arena counts both injected failures and the arena's own. permit replaces that budget without resetting evidence. Free calls delegate to the parent; in-place growth is refused so allocation-failure tests remain predictable.

Items

Executable example

This complete program is maintained in the repository runtime tests. View source.

import core/mem
import core/fault

exercise: () -> (ok: bool) ! mem.out_of_memory =
    ok = false
    mut backing: [32]u8 = zeroed
    mut inner := mem.arena_over_unchecked(addr backing[0], 32)
    mut state := fault.new(addr inner, usize(1))
    address := try mem.allocate(state, usize(4), usize(1))
    address.val = 42
    return when address.val <> 42
    _ = mem.allocate(state, usize(4), usize(1)) else (problem)
        _ = problem
        ok = fault.successes(state) == 1 and fault.injected_failures(state) == 1
        mem.free(state, address, usize(4))
        return
    end
end exercise

public main: () -> (code: i32) =
    code = 1
    ok := exercise() else false
    if ok then
        code = 42
    end if
end main

injector type

public injector: type (inner_provider: type) = struct
    inner: ptr mut inner_provider
    remaining: usize
    attempts: usize
    delegated: usize
    successes: usize
    injected_failures: usize
    inner_failures: usize
    frees: usize
    live: usize
end injector

core/fault/fault.ldn:9

Allocator wrapper with deterministic failure injection and cumulative counters. Borrows its inner provider, delegates permitted allocations and frees, and refuses in-place growth.

attempts function

public attempts: (inner_provider: type,
                  state: injector(inner_provider)) -> (count: usize)

core/fault/fault.ldn:102

Return all allocation attempts, including injected and inner failures.

delegated function

public delegated: (inner_provider: type,
                   state: injector(inner_provider)) -> (count: usize)

core/fault/fault.ldn:108

Return allocation attempts passed through to the inner provider.

frees function

public frees: (inner_provider: type,
               state: injector(inner_provider)) -> (count: usize)

core/fault/fault.ldn:133

Return the number of delegated free calls.

injected_failures function

public injected_failures: (inner_provider: type,
                           state: injector(inner_provider))
                          -> (count: usize)

core/fault/fault.ldn:120

Return attempts rejected by the wrapper before calling the inner provider.

inner_failures function

public inner_failures: (inner_provider: type,
                        state: injector(inner_provider)) -> (count: usize)

core/fault/fault.ldn:127

Return allocation failures reported by the inner provider.

live function

public live: (inner_provider: type,
              state: injector(inner_provider)) -> (count: usize)

core/fault/fault.ldn:141

Return successful allocations minus free calls, bounded below by zero. Exact under the allocator contract; malformed or duplicate frees are not validated here.

new function

public new: (inner_provider: type is mem.allocator,
             inner: ptr mut inner_provider,
             allowed_attempts: usize)
            -> (state: injector(inner_provider) from inner)

core/fault/fault.ldn:25

Wrap an allocator with a budget of allowed_attempts allocation calls that may reach it. An inner failure consumes one of them too; an injected failure never calls the inner provider and counts only in attempts and injected_failures.

permit function

public permit: (inner_provider: type,
                inout state: injector(inner_provider),
                allowed_attempts: usize) -> none

core/fault/fault.ldn:37

Replace the future delegation budget without resetting the counters, so failed and successful calls stay distinguishable across retries.

remaining function

public remaining: (inner_provider: type,
                   state: injector(inner_provider)) -> (count: usize)

core/fault/fault.ldn:96

Return how many allocation attempts may still reach the inner provider.

successes function

public successes: (inner_provider: type,
                   state: injector(inner_provider)) -> (count: usize)

core/fault/fault.ldn:114

Return the number of successful delegated allocations.