// Zig wrappers for ESP-IDF's POSIX pthread implementation (backed by FreeRTOS). // // Symbols come from idf-sys.zig (generated by `zig translate-c include/stubs.h` // with ESP_IDF_COMP_PTHREAD_ENABLED defined, which pulls in pthread.h and // esp_pthread.h). // // Error handling: // POSIX pthread_* functions return 0 on success, errno on failure → `check()`. // ESP-IDF esp_pthread_* functions return esp_err_t → `errors.espCheckError()`. const sys = @import("sys"); const errors = @import("error"); // --------------------------------------------------------------------------- // Error helper for POSIX-style return codes // --------------------------------------------------------------------------- pub const Error = error{PthreadError}; fn check(rc: c_int) Error!void { if (rc != 0) return error.PthreadError; } // --------------------------------------------------------------------------- // Type aliases (from idf-sys) // --------------------------------------------------------------------------- pub const Thread_t = sys.pthread_t; pub const Attr_t = sys.pthread_attr_t; pub const Mutex_t = sys.pthread_mutex_t; pub const MutexAttr_t = sys.pthread_mutexattr_t; pub const Cond_t = sys.pthread_cond_t; pub const CondAttr_t = sys.pthread_condattr_t; pub const Key_t = sys.pthread_key_t; pub const Once_t = sys.pthread_once_t; pub const SchedParam = sys.sched_param; pub const Timespec = sys.timespec; /// ESP-IDF-specific pthread configuration (stack size, priority, core pinning, etc.). pub const EspCfg = sys.esp_pthread_cfg_t; // --------------------------------------------------------------------------- // Thread — lifecycle & identity // --------------------------------------------------------------------------- pub const Thread = struct { /// Create a thread with optional attributes. Returns the thread ID. pub fn create( attr: ?*const Attr_t, start_fn: *const fn (?*anyopaque) callconv(.c) ?*anyopaque, arg: ?*anyopaque, ) !Thread_t { var tid: Thread_t = undefined; try check(sys.pthread_create(&tid, attr, start_fn, arg)); return tid; } /// Wait for a thread to terminate; returns its exit value. pub fn join(tid: Thread_t) !?*anyopaque { var value: ?*anyopaque = null; try check(sys.pthread_join(tid, &value)); return value; } /// Detach a thread so its resources are freed automatically on exit. pub fn detach(tid: Thread_t) !void { try check(sys.pthread_detach(tid)); } /// Terminate the calling thread with `retval`. pub fn exit(retval: ?*anyopaque) noreturn { sys.pthread_exit(retval); unreachable; } /// Return the thread ID of the calling thread. pub fn self() Thread_t { return sys.pthread_self(); } /// Return `true` if two thread IDs refer to the same thread. pub fn equal(t1: Thread_t, t2: Thread_t) bool { return sys.pthread_equal(t1, t2) != 0; } /// Yield the processor to another ready thread. pub fn yield() void { sys.pthread_yield(); } }; // --------------------------------------------------------------------------- // Thread attributes // --------------------------------------------------------------------------- pub const Attr = struct { pub fn init(attr: *Attr_t) !void { try check(sys.pthread_attr_init(attr)); } pub fn deinit(attr: *Attr_t) !void { try check(sys.pthread_attr_destroy(attr)); } pub fn setStackSize(attr: *Attr_t, size: usize) !void { try check(sys.pthread_attr_setstacksize(attr, size)); } pub fn getStackSize(attr: *const Attr_t) !usize { var sz: usize = 0; try check(sys.pthread_attr_getstacksize(attr, &sz)); return sz; } pub fn setDetachState(attr: *Attr_t, state: c_int) !void { try check(sys.pthread_attr_setdetachstate(attr, state)); } pub fn getDetachState(attr: *const Attr_t) !c_int { var s: c_int = 0; try check(sys.pthread_attr_getdetachstate(attr, &s)); return s; } pub fn setSchedParam(attr: *Attr_t, param: *const SchedParam) !void { try check(sys.pthread_attr_setschedparam(attr, param)); } pub fn getSchedParam(attr: *const Attr_t) !SchedParam { var p: SchedParam = .{}; try check(sys.pthread_attr_getschedparam(attr, &p)); return p; } }; // --------------------------------------------------------------------------- // Mutex // --------------------------------------------------------------------------- pub const Mutex = struct { pub fn init(mutex: *Mutex_t, attr: ?*const MutexAttr_t) !void { try check(sys.pthread_mutex_init(mutex, attr)); } pub fn deinit(mutex: *Mutex_t) !void { try check(sys.pthread_mutex_destroy(mutex)); } pub fn lock(mutex: *Mutex_t) !void { try check(sys.pthread_mutex_lock(mutex)); } pub fn tryLock(mutex: *Mutex_t) !void { try check(sys.pthread_mutex_trylock(mutex)); } pub fn unlock(mutex: *Mutex_t) !void { try check(sys.pthread_mutex_unlock(mutex)); } pub fn timedLock(mutex: *Mutex_t, timeout: *const Timespec) !void { try check(sys.pthread_mutex_timedlock(mutex, timeout)); } }; // --------------------------------------------------------------------------- // Condition variable // --------------------------------------------------------------------------- pub const Cond = struct { pub fn init(cond: *Cond_t, attr: ?*const CondAttr_t) !void { try check(sys.pthread_cond_init(cond, attr)); } pub fn deinit(cond: *Cond_t) !void { try check(sys.pthread_cond_destroy(cond)); } pub fn wait(cond: *Cond_t, mutex: *Mutex_t) !void { try check(sys.pthread_cond_wait(cond, mutex)); } pub fn timedWait(cond: *Cond_t, mutex: *Mutex_t, abstime: *const Timespec) !void { try check(sys.pthread_cond_timedwait(cond, mutex, abstime)); } pub fn signal(cond: *Cond_t) !void { try check(sys.pthread_cond_signal(cond)); } pub fn broadcast(cond: *Cond_t) !void { try check(sys.pthread_cond_broadcast(cond)); } }; // --------------------------------------------------------------------------- // Thread-local storage (TLS) keys // --------------------------------------------------------------------------- pub const Key = struct { pub fn create(destructor: ?*const fn (?*anyopaque) callconv(.c) void) !Key_t { var key: Key_t = undefined; try check(sys.pthread_key_create(&key, destructor)); return key; } pub fn delete(key: Key_t) !void { try check(sys.pthread_key_delete(key)); } pub fn setSpecific(key: Key_t, value: ?*const anyopaque) !void { try check(sys.pthread_setspecific(key, value)); } pub fn getSpecific(key: Key_t) ?*anyopaque { return sys.pthread_getspecific(key); } }; // --------------------------------------------------------------------------- // Once-only initialisation // --------------------------------------------------------------------------- pub fn once(once_ctrl: *Once_t, init_fn: *const fn () callconv(.c) void) !void { try check(sys.pthread_once(once_ctrl, init_fn)); } // --------------------------------------------------------------------------- // ESP-IDF pthread extensions (esp_pthread.h) // --------------------------------------------------------------------------- pub const EspThread = struct { /// Return a default ESP pthread configuration populated from menuconfig values. pub fn getDefaultConfig() EspCfg { return sys.esp_pthread_get_default_config(); } /// Set per-thread creation parameters for the next `pthread_create()` call. /// Overrides stack size, priority, core affinity, and thread name. pub fn setConfig(cfg: *const EspCfg) !void { try errors.espCheckError(sys.esp_pthread_set_cfg(cfg)); } /// Retrieve the currently active ESP pthread configuration. pub fn getConfig(cfg: *EspCfg) !void { try errors.espCheckError(sys.esp_pthread_get_cfg(cfg)); } /// Initialise the ESP pthread library (called internally by ESP-IDF startup). pub fn init() !void { try errors.espCheckError(sys.esp_pthread_init()); } };