/* * Revera — a real-time kernel with hardware-enforced isolation. * Copyright (c) 2026 VIACERTO LLC. All rights reserved. * * This file is dual-licensed under the PolyForm Noncommercial License 1.0.0 OR * a commercial license from VIACERTO LLC. You may choose to use this file under * the terms of either license. * * For the full license terms, see the LICENSE and LICENSE-COMMERCIAL.md files in * the root directory of this source tree. * * SPDX-FileCopyrightText: © 2026 VIACERTO LLC * SPDX-License-Identifier: LicenseRef-PolyForm-Noncommercial-1.0.0 OR LicenseRef-VIACERTO-Commercial */ /* * compat/pthread.h — an OPT-IN, pthread-SHAPED shim over the tk kernel API. * * NOT POSIX-compliant. It gives a pthreads engineer a familiar surface for the subset of tk that is * genuinely pthread-shaped, so embedded-Linux thread code ports MECHANICALLY (change #includes, provide a * static stack per thread, drop async cancellation). The kernel keeps its own model underneath — opaque * kernel-owned handles + static pools + caller-provided stacks — which is exactly what makes UNPRIV * threads safe; this header is a thin translation, not a re-implementation. * * HOW TO USE: put this directory on the include path AHEAD of any system header (the example does * `CFLAGS += -I/compat`) and `#include `. Deliberately opt-in and per-target: it is NOT on * the default include path, so ordinary tk builds never see it. * * NOT FOR THE HOST. BOARD=host is arch/pthread — REAL pthreads — so this shim would collide with the * system . The guard below fails the build loud on a hosted platform. Bare-metal cross targets * (arm-none-eabi / riscv / tricore …) define none of those macros and compile through. * * WHAT MAPS (see docs/evaluation/standards-api-ergonomics.md for the full analysis): * pthread_create/join/detach/exit/self/equal, sched_yield, pthread_attr_* (with setstack REQUIRED — * tk does not allocate stacks), pthread_mutex_* + pthread_mutexattr_* (init/type/protocol), pthread_cond_*. * WHAT DOES NOT: PTHREAD_MUTEX_INITIALIZER (a pool can't static-init → use pthread_mutex_init), a default * (NULL / no-stack) pthread_create (you must supply a stack), pthread_cancel / TLS / pthread_once * (omitted by design or not built), and abstime timedwaits (tk uses relative ms). */ #ifndef TK_COMPAT_PTHREAD_H #define TK_COMPAT_PTHREAD_H #if defined(__unix__) || defined(__APPLE__) || defined(_WIN32) || defined(__CYGWIN__) #error "compat/pthread.h is the tk pthread-SHAPED shim for BARE-METAL targets. On a hosted platform \ (incl. BOARD=host, which is arch/pthread = real pthreads) use the system . Do not put this \ directory on the include path for a host build." #endif #include #include #include #include "tk_kernel.h" #include "tk_mutex.h" #include "tk_cond.h" /* ── types: a pthread_* handle IS the tk kernel object (opaque, either way) ── */ typedef tk_thread_t* pthread_t; typedef tk_thread_attr_t pthread_attr_t; typedef tk_mutex_t pthread_mutex_t; typedef tk_mutexattr_t pthread_mutexattr_t; typedef tk_cond_t pthread_cond_t; typedef int pthread_condattr_t; /* tk cond has no attributes; accepted + ignored */ #ifndef SCHED_PARAM_DEFINED #define SCHED_PARAM_DEFINED struct sched_param { int sched_priority; }; #endif /* ── constants ── */ #define PTHREAD_CREATE_JOINABLE TK_THREAD_CREATE_JOINABLE #define PTHREAD_CREATE_DETACHED TK_THREAD_CREATE_DETACHED #define PTHREAD_MUTEX_NORMAL TK_MUTEX_NORMAL #define PTHREAD_MUTEX_RECURSIVE TK_MUTEX_RECURSIVE #define PTHREAD_MUTEX_ERRORCHECK TK_MUTEX_ERRORCHECK #define PTHREAD_PRIO_NONE TK_MUTEX_PRIO_NONE #define PTHREAD_PRIO_INHERIT TK_MUTEX_PRIO_INHERIT /* tk_status_t -> pthread int (0 = success, else an errno the caller can test). "Shaped, not compliant": * the mapping is best-effort onto standard errno values. */ static inline int tk__pthread_err(tk_status_t s) { switch (s) { case TK_OK: return 0; case TK_ERR_PARAM: return EINVAL; case TK_ERR_BUSY: return EBUSY; case TK_ERR_TIMEOUT: return ETIMEDOUT; case TK_ERR_UNSUPPORTED: return ENOSYS; case TK_ERR_STATE: return EAGAIN; /* e.g. the static thread/sync pool is full */ default: return EAGAIN; } } /* ── threads ── */ /* pthread_create: the ONE call that cannot be a drop-in — tk allocates no stacks, so `attr` MUST carry a * caller-provided stack (pthread_attr_setstack). A NULL/default attr has no stack and is rejected. */ static inline int pthread_create(pthread_t* thread, const pthread_attr_t* attr, void* (*start_routine)(void*), void* arg) { if (attr == NULL) { return EINVAL; } /* must set a stack via pthread_attr_setstack */ return tk__pthread_err(tk_thread_spawn(thread, attr, start_routine, arg)); } static inline int pthread_join(pthread_t thread, void** retval) { return tk_thread_join(thread, retval); } static inline int pthread_detach(pthread_t thread) { tk_thread_detach(thread); return 0; } static inline void pthread_exit(void* retval) { tk_thread_exit(retval); } /* noreturn */ static inline pthread_t pthread_self(void) { return tk_thread_self(); } /* Handles are unique kernel objects, so identity is a pointer compare. */ static inline int pthread_equal(pthread_t a, pthread_t b) { return a == b; } static inline int sched_yield(void) { tk_thread_yield(); return 0; } #define pthread_yield sched_yield /* ── thread attributes ── */ static inline int pthread_attr_init(pthread_attr_t* attr) { tk_thread_attr_init(attr); return 0; } static inline int pthread_attr_destroy(pthread_attr_t* attr) { (void)attr; return 0; } /* nothing allocated */ static inline int pthread_attr_setdetachstate(pthread_attr_t* attr, int state) { tk_thread_attr_setdetachstate(attr, state); return 0; } static inline int pthread_attr_getdetachstate(const pthread_attr_t* attr, int* state) { *state = (int)attr->detachstate; return 0; } /* POSIX pthread_attr_setstack(attr, addr, size). tk sizes stacks in ADDRESSABLE CELLS (== bytes on a * byte-addressable arch; 16-bit words on C2000) — pass the size in cells. This is how you satisfy the * "caller provides the stack" contract. */ static inline int pthread_attr_setstack(pthread_attr_t* attr, void* stackaddr, size_t stackcells) { tk_thread_attr_setstack(attr, stackaddr, stackcells); return 0; } static inline int pthread_attr_setschedparam(pthread_attr_t* attr, const struct sched_param* param) { tk_thread_attr_setpriority(attr, (unsigned char)param->sched_priority); return 0; } /* ── mutexes ── */ static inline int pthread_mutex_init(pthread_mutex_t* m, const pthread_mutexattr_t* attr) { return tk_mutex_init(m, attr); } static inline int pthread_mutex_destroy(pthread_mutex_t* m) { (void)m; return 0; } /* pooled; no free */ static inline int pthread_mutex_lock(pthread_mutex_t* m) { tk_mutex_lock(*m); return 0; } static inline int pthread_mutex_unlock(pthread_mutex_t* m) { tk_mutex_unlock(*m); return 0; } static inline int pthread_mutex_trylock(pthread_mutex_t* m) { return (tk_mutex_lock_timeout(*m, 0u) == 0) ? 0 : EBUSY; } static inline int pthread_mutexattr_init(pthread_mutexattr_t* a) { tk_mutexattr_init(a); return 0; } static inline int pthread_mutexattr_destroy(pthread_mutexattr_t* a) { (void)a; return 0; } static inline int pthread_mutexattr_settype(pthread_mutexattr_t* a, int type) { tk_mutexattr_settype(a, type); return 0; } static inline int pthread_mutexattr_setprotocol(pthread_mutexattr_t* a, int protocol) { tk_mutexattr_setprotocol(a, protocol); return 0; } /* ── condition variables ── */ static inline int pthread_cond_init(pthread_cond_t* c, const pthread_condattr_t* attr) { (void)attr; *c = tk_cond_create(); return (*c == TK_COND_INVALID) ? EAGAIN : 0; } static inline int pthread_cond_destroy(pthread_cond_t* c) { (void)c; return 0; } /* pooled; no free */ static inline int pthread_cond_wait(pthread_cond_t* c, pthread_mutex_t* m) { tk_cond_wait(*c, *m); return 0; } static inline int pthread_cond_signal(pthread_cond_t* c) { tk_cond_signal(*c); return 0; } static inline int pthread_cond_broadcast(pthread_cond_t* c) { tk_cond_broadcast(*c); return 0; } #endif /* TK_COMPAT_PTHREAD_H */