Skip to documentation
SLOP

tiny.sys.thread

Reference tiny.sys thread

Defined in tiny.sys.

API (45)

Actions

Public operations.

Types and contracts

Public types and contracts.

Values and defaults

Public values and defaults.

No direct callersNo direct callstiny.systhread
Static calls · unresolved targets: unknown · external targets: unknown.

Source

Called byCallsNo direct callersprivate sourcelib.sys.src.threadthreadedIothread.Conditionbroadcast
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsNo direct callersprivate sourcelib.sys.src.threadthreadedIothread.Conditionsignal
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsNo direct callersprivate sourcelib.sys.src.threadthreadedIothread.Conditionwait
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallstest sourcelib.sys.src.threadtest: semaphore routes through sys th...private sourcelib.sys.src.threadthreadedIothread.Semaphorepost
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallstest sourcelib.sys.src.threadtest: semaphore routes through sys th...private sourcelib.sys.src.threadthreadedIothread.Semaphorewait
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsprivate sourcelib.sys.src.thread.FutexProbewaitprivate sourcelib.sys.src.threadthreadedIothreadblockUntilDifferent
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallstest sourcelib.sys.src.threadtest: thread support follows host cap...threadthreadsSupportedthreadcpuCount
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstest sourcelib.sys.src.threadtest: native thread-specific destruct...test sourcelib.sys.src.threadtest: native thread-specific values a...threadthreadSpecificDestructorsSupportedthreadcreateThreadSpecificKey
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallsNo direct callstest sourcelib.sys.src.threadtest: current placement translates th...threadcurrentAffinity
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallstest sourcelib.sys.src.threadtest: thread support follows host cap...threadthreadsSupportedthreadcurrentId
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstest sourcelib.sys.src.threadtest: current placement translates th...private sourcelib.sys.src.linuxerrnothreadcurrentPlacement
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallsprivate sourcelib.sys.src.thread.ThreadSpecificProbeisolatetest sourcelib.sys.src.threadtest: native thread-specific values a...threadthreadSpecificDestructorsSupportedthreadgetThreadSpecificValue
Static calls · unresolved targets: 1 · external targets: 0.
Called byCallsprivate sourcelib.sys.src.thread.ThreadSpecificProbeisolateprivate sourcelib.sys.src.thread.ThreadSpecificProbesetValuetest sourcelib.sys.src.threadtest: native thread-specific values a...threadthreadSpecificDestructorsSupportedthreadsetThreadSpecificValue
Static calls · unresolved targets: 0 · external targets: 2.
Called byCallstiny.httptiny.sysThreadPooltest sourcelib.http.src.pooltest: ThreadPool accepts concurrent p...test sourcelib.http.src.server.testtest: Server accepts connectiontest sourcelib.http.src.server.testtest: Server closes overload and reus...test sourcelib.http.src.server.testtest: Server expires idle connections...+7 moreprivate sourcelib.sys.src.threadrunNativeThreadSpecificDestructorsthreadthreadsSupportedthreadspawn
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstest sourcelib.sys.src.threadtest: native thread-specific destruct...test sourcelib.sys.src.threadtest: native thread-specific values a...test sourcelib.sys.src.threadtest: thread-specific destructor supp...threadthreadSpecificDestructorsSupportedthreadthreadsSupportedthreadthreadSpecificDestructorPolicy
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsthreadcreateThreadSpecificKeythreadgetThreadSpecificValueprivate sourcelib.sys.src.threadrunNativeThreadSpecificDestructorsthreadsetThreadSpecificValuetest sourcelib.sys.src.threadtest: thread-specific destructor supp...threadthreadSpecificDestructorPolicythreadthreadSpecificDestructorsSupported
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callsthreadcpuCountthreadcurrentIdthreadspawntest sourcelib.sys.src.threadtest: futex boundary waits on and wak...test sourcelib.sys.src.threadtest: thread-specific destructor supp...+3 morethreadthreadsSupported
Static calls · unresolved targets: 0 · external targets: 1.
Called byCallstest sourcelib.sys.src.threadtest: futex boundary waits on and wak...private sourcelib.sys.src.threadthreadedIothreadwakeAll
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsprivate sourcelib.http.src.poollockprivate sourcelib.http.src.server.runtimelockthreadthreadsSupportedthreadyield
Static calls · unresolved targets: 0 · external targets: 0.

Source: lib/sys/src/root.zig:52

zig
pub const thread = @import("thread.zig");

Source: lib/sys/src/thread.zig

zig
const std = @import("std");const builtin = @import("builtin");const sys_allocator = @import("allocator.zig");const capabilities = @import("capabilities.zig");const linux = std.os.linux;const native_os = builtin.os.tag;const posix = std.posix;pub const required_capabilities = capabilities.host(&.{.threads});pub const ThreadSpecificError = error{    UnsupportedPlatform,    SystemResources,};pub const SpawnError = error{    UnsupportedPlatform,    OutOfMemory,    SystemResources,};pub const Placement = struct {    cpu_id: usize,    numa_node: usize,};pub const PlacementError = error{ UnsupportedPlatform, QueryFailed };pub const CpuSet = linux.cpu_set_t;pub const AffinityError = error{ UnsupportedPlatform, QueryFailed, ApplyFailed };pub const ThreadSpecificDestructor = *const fn (value: *anyopaque) callconv(.c) void;pub const Id = std.Thread.Id;pub const ThreadedIo = std.Io.Threaded;pub const ThreadedIoOptions = std.Io.Threaded.InitOptions;pub const ThreadSpecificDestructorPolicy = enum {    unsupported,    native_spawn_exit,};fn threadedIo() std.Io {    return std.Io.Threaded.global_single_threaded.io();}pub fn initThreadedIo(allocator: std.mem.Allocator, options: ThreadedIoOptions) ThreadedIo {    return ThreadedIo.init(allocator, options);}pub fn initProcessThreadedIo(allocator: std.mem.Allocator) ThreadedIo {    return ThreadedIo.init(allocator, .{        .environ = @import("env.zig").processEnviron(),    });}pub const Mutex = struct {    raw: std.Io.Mutex = .init,    pub fn tryLock(self: *Mutex) bool {        return self.raw.tryLock();    }    pub fn lock(self: *Mutex) void {        std.Io.Threaded.mutexLock(&self.raw);    }    pub fn unlock(self: *Mutex) void {        std.Io.Threaded.mutexUnlock(&self.raw);    }};pub const Condition = struct {    raw: std.Io.Condition = .init,    pub fn wait(self: *Condition, mutex: *Mutex) void {        self.raw.waitUncancelable(threadedIo(), &mutex.raw);    }    pub fn signal(self: *Condition) void {        self.raw.signal(threadedIo());    }    pub fn broadcast(self: *Condition) void {        self.raw.broadcast(threadedIo());    }};pub const Semaphore = struct {    raw: std.Io.Semaphore = .{},    pub fn post(self: *Semaphore) void {        self.raw.post(threadedIo());    }    pub fn wait(self: *Semaphore) void {        self.raw.waitUncancelable(threadedIo());    }};pub const ThreadSpecificKey = struct {    index: usize = invalid_thread_specific_key,};const invalid_thread_specific_key = std.math.maxInt(usize);const native_thread_specific_destructor_iterations = 4;const NativeThreadSpecificRegistry = struct {    mutex: std.atomic.Mutex = .unlocked,    destructors: std.ArrayList(ThreadSpecificDestructor) = .empty,    fn createKey(self: *NativeThreadSpecificRegistry, allocator: std.mem.Allocator, callback: ThreadSpecificDestructor) ThreadSpecificError!ThreadSpecificKey {        self.lock();        defer self.mutex.unlock();        const index = self.destructors.items.len;        if (index == invalid_thread_specific_key) return error.SystemResources;        self.destructors.append(allocator, callback) catch return error.SystemResources;        return .{ .index = index };    }    fn destructor(self: *NativeThreadSpecificRegistry, key_index: usize) ?ThreadSpecificDestructor {        self.lock();        defer self.mutex.unlock();        if (key_index >= self.destructors.items.len) return null;        return self.destructors.items[key_index];    }    fn lock(self: *NativeThreadSpecificRegistry) void {        while (!self.mutex.tryLock()) std.atomic.spinLoopHint();    }};const NativeThreadSpecificValues = struct {    values: std.ArrayList(?*anyopaque) = .empty,    fn set(self: *NativeThreadSpecificValues, allocator: std.mem.Allocator, key: ThreadSpecificKey, value: ?*anyopaque) bool {        if (key.index == invalid_thread_specific_key) return false;        if (value == null and key.index >= self.values.items.len) return true;        while (self.values.items.len <= key.index) {            self.values.append(allocator, null) catch return false;        }        self.values.items[key.index] = value;        return true;    }    fn get(self: *NativeThreadSpecificValues, key: ThreadSpecificKey) ?*anyopaque {        if (key.index == invalid_thread_specific_key or key.index >= self.values.items.len) return null;        return self.values.items[key.index];    }    fn runDestructors(self: *NativeThreadSpecificValues, allocator: std.mem.Allocator) void {        defer {            self.values.deinit(allocator);            self.* = .{};        }        var iteration: usize = 0;        while (iteration < native_thread_specific_destructor_iterations) : (iteration += 1) {            var called = false;            var index: usize = 0;            while (index < self.values.items.len) : (index += 1) {                const value = self.values.items[index] orelse continue;                const destructor = native_thread_specific_registry.destructor(index) orelse continue;                self.values.items[index] = null;                called = true;                destructor(value);            }            if (!called) break;        }    }};var native_thread_specific_registry: NativeThreadSpecificRegistry = .{};threadlocal var native_thread_specific_values: NativeThreadSpecificValues = .{};pub const JoinHandle = struct {    raw: std.Thread,    pub fn join(self: JoinHandle) void {        self.raw.join();    }    pub fn detach(self: JoinHandle) void {        self.raw.detach();    }    pub fn setName(        self: JoinHandle,        name: []const u8,    ) std.Thread.SetNameError!void {        try self.raw.setName(std.Options.debug_io, name);    }};pub fn blockUntilDifferent(    previous: u32,    current: *const std.atomic.Value(u32),) u32 {    while (true) {        const next = current.load(.acquire);        if (next != previous) return next;        std.Io.futexWaitUncancelable(threadedIo(), u32, &current.raw, previous);    }}pub fn wakeAll(current: *std.atomic.Value(u32)) void {    std.Io.futexWake(threadedIo(), u32, &current.raw, std.math.maxInt(u32));}pub fn threadsSupported() bool {    return comptime capabilities.current.supportsThreads();}pub fn spawn(comptime func: anytype, args: anytype) SpawnError!JoinHandle {    if (comptime !threadsSupported()) return error.UnsupportedPlatform;    const Runner = struct {        fn run(captured_args: @TypeOf(args)) void {            defer runNativeThreadSpecificDestructors();            _ = @call(.auto, func, captured_args);        }    };    return .{ .raw = std.Thread.spawn(.{}, Runner.run, .{args}) catch |err| switch (err) {        error.OutOfMemory => return error.OutOfMemory,        else => return error.SystemResources,    } };}pub fn yield() void {    if (comptime !threadsSupported()) return;    std.Thread.yield() catch {};}pub fn cpuCount() usize {    if (comptime !threadsSupported()) return 1;    return std.Thread.getCpuCount() catch 1;}pub fn currentId() Id {    if (comptime !threadsSupported()) return 0;    return std.Thread.getCurrentId();}pub fn currentPlacement() PlacementError!Placement {    if (comptime native_os != .linux) return error.UnsupportedPlatform;    var cpu_id: usize = 0;    var numa_node: usize = 0;    return switch (linux.errno(linux.getcpu(&cpu_id, &numa_node))) {        .SUCCESS => .{ .cpu_id = cpu_id, .numa_node = numa_node },        .NOSYS => error.UnsupportedPlatform,        else => error.QueryFailed,    };}pub fn currentAffinity() AffinityError!CpuSet {    if (comptime native_os != .linux) return error.UnsupportedPlatform;    return posix.sched_getaffinity(0) catch return error.QueryFailed;}pub fn setCurrentAffinity(set: *const CpuSet) AffinityError!void {    if (comptime native_os != .linux) return error.UnsupportedPlatform;    linux.sched_setaffinity(0, set) catch return error.ApplyFailed;}pub fn threadSpecificDestructorPolicy() ThreadSpecificDestructorPolicy {    if (comptime threadsSupported()) return .native_spawn_exit;    return .unsupported;}pub fn threadSpecificDestructorsSupported() bool {    return threadSpecificDestructorPolicy() != .unsupported;}pub fn createThreadSpecificKey(destructor: ThreadSpecificDestructor) ThreadSpecificError!ThreadSpecificKey {    if (comptime !threadSpecificDestructorsSupported()) return error.UnsupportedPlatform;    return native_thread_specific_registry.createKey(sys_allocator.processAllocator(), destructor);}pub fn setThreadSpecificValue(key: ThreadSpecificKey, value: ?*anyopaque) bool {    if (comptime !threadSpecificDestructorsSupported()) return false;    return native_thread_specific_values.set(sys_allocator.processAllocator(), key, value);}pub fn getThreadSpecificValue(key: ThreadSpecificKey) ?*anyopaque {    if (comptime !threadSpecificDestructorsSupported()) return null;    return native_thread_specific_values.get(key);}fn runNativeThreadSpecificDestructors() void {    if (comptime !threadSpecificDestructorsSupported()) return;    native_thread_specific_values.runDestructors(sys_allocator.processAllocator());}test "thread-specific destructor support uses sys-owned thread exit policy" {    const expected: ThreadSpecificDestructorPolicy = if (comptime threadsSupported()) .native_spawn_exit else .unsupported;    try std.testing.expectEqual(expected, threadSpecificDestructorPolicy());    try std.testing.expectEqual(expected != .unsupported, threadSpecificDestructorsSupported());}test "thread support follows host capabilities" {    try std.testing.expectEqual(capabilities.current.supportsThreads(), threadsSupported());    try std.testing.expect(cpuCount() >= 1);    try std.testing.expectEqual(currentId(), currentId());}test "current placement translates the host query" {    if (native_os != .linux) {        try std.testing.expectError(error.UnsupportedPlatform, currentPlacement());        return;    }    const placement = try currentPlacement();    try std.testing.expect(placement.cpu_id < std.math.maxInt(usize));    try std.testing.expect(placement.numa_node < std.math.maxInt(usize));    _ = try currentAffinity();}test "semaphore routes through sys thread io policy" {    var semaphore: Semaphore = .{};    semaphore.post();    semaphore.wait();}const FutexProbe = struct {    current: *std.atomic.Value(u32),    ready: *std.atomic.Value(bool),    observed: *u32,    fn wait(self: FutexProbe) void {        self.ready.store(true, .release);        self.observed.* = blockUntilDifferent(7, self.current);    }};test "futex boundary waits on and wakes an atomic word" {    if (!threadsSupported()) return error.SkipZigTest;    var current = std.atomic.Value(u32).init(7);    var ready = std.atomic.Value(bool).init(false);    var observed: u32 = 0;    const handle = try spawn(FutexProbe.wait, .{        FutexProbe{            .current = &current,            .ready = &ready,            .observed = &observed,        },    });    while (!ready.load(.acquire)) std.atomic.spinLoopHint();    current.store(11, .release);    wakeAll(&current);    handle.join();    try std.testing.expectEqual(@as(u32, 11), observed);}const ThreadSpecificProbe = struct {    fn destroy(value: *anyopaque) callconv(.c) void {        const counter: *std.atomic.Value(usize) = @ptrCast(@alignCast(value));        _ = counter.fetchAdd(1, .monotonic);    }    fn setValue(key: ThreadSpecificKey, counter: *std.atomic.Value(usize)) void {        if (!setThreadSpecificValue(key, counter)) @panic("thread specific set failed");    }    fn isolate(key: ThreadSpecificKey, expected_missing: *bool, value: *usize, result: *usize) void {        expected_missing.* = getThreadSpecificValue(key) == null;        if (!setThreadSpecificValue(key, value)) @panic("thread specific set failed");        result.* = @intFromPtr(getThreadSpecificValue(key) orelse return);    }};test "native thread-specific destructor runs when sys-spawned thread exits" {    if (comptime threadSpecificDestructorPolicy() != .native_spawn_exit) return error.SkipZigTest;    const key = try createThreadSpecificKey(ThreadSpecificProbe.destroy);    var counter = std.atomic.Value(usize).init(0);    const thread = try spawn(ThreadSpecificProbe.setValue, .{ key, &counter });    thread.join();    try std.testing.expectEqual(@as(usize, 1), counter.load(.monotonic));}test "native thread-specific values are isolated per sys thread" {    if (comptime threadSpecificDestructorPolicy() != .native_spawn_exit) return error.SkipZigTest;    const key = try createThreadSpecificKey(ThreadSpecificProbe.destroy);    var main_counter = std.atomic.Value(usize).init(0);    try std.testing.expect(setThreadSpecificValue(key, &main_counter));    defer _ = setThreadSpecificValue(key, null);    var thread_missing_main = false;    var thread_value: usize = 0;    var thread_result: usize = 0;    const thread = try spawn(ThreadSpecificProbe.isolate, .{ key, &thread_missing_main, &thread_value, &thread_result });    thread.join();    try std.testing.expect(thread_missing_main);    try std.testing.expectEqual(@intFromPtr(&thread_value), thread_result);    try std.testing.expectEqual(@intFromPtr(&main_counter), @intFromPtr(getThreadSpecificValue(key) orelse return error.MainThreadSpecificValueMissing));}

Complete caller list for thread.spawn

12 direct callers.

Complete caller list for thread.threadsSupported

8 direct callers.

Audit

Definitions46
Public names46
Members19
Version26.7.0
Revisiondaab053ee433