SuperTinyKernel™ RTOS 1.08.x
Lightweight, high-performance, deterministic, bare-metal C++ RTOS for resource-constrained embedded systems. MIT Open Source License.
Loading...
Searching...
No Matches
stk_c_pthread.h File Reference

A minimal, POSIX-named pthreads-style API for STK. The public interface needs only stk_c.h / stk_c_memory.h (no C++ headers), so it can be used from C. The implementation (stk_c_pthread.cpp) calls the STK C++ API directly for thread/task control, TLS, critical sections, sleep/yield/time and the synchronization primitives; the stk_*_mem_t blobs embedded in the pthread_* types are used as in-place storage for those C++ objects. More...

#include "stk_c.h"
#include <ctime>
Include dependency graph for stk_c_pthread.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Classes

struct  pthread_attr_t
 Thread creation attributes. More...
struct  pthread_mutexattr_t
 Mutex attributes. More...
struct  pthread_mutex_t
 A pthread mutex. More...
struct  pthread_condattr_t
 Condition variable attributes (currently no settable properties). More...
struct  pthread_cond_t
 A pthread condition variable. More...
struct  pthread_rwlockattr_t
 Read-write lock attributes (currently no settable properties). More...
struct  pthread_rwlock_t
 A pthread read-write lock. More...
struct  pthread_spinlock_t
 A pthread spinlock. More...
struct  pthread_barrierattr_t
 Barrier attributes (currently no settable properties). More...
struct  pthread_barrier_t
 A pthread barrier. More...
struct  pthread_once_t
 A pthread_once() control object. More...

Macros

#define _SYS__PTHREADTYPES_H_
#define STK_C_PTHREAD_VERSION   (0x20261004)
 Interface version.
#define STK_C_PTHREAD_MAX_THREADS   (8U)
 Maximum number of concurrently-alive pthread_t's (default: 8).
#define STK_C_PTHREAD_DEFAULT_STACK_WORDS   (1024U)
 Default per-thread stack size in stk_word_t units, used when pthread_attr_t does not specify a stack size or an external stack (default: 1024 words).
#define STK_C_PTHREAD_REAPER_STACK_WORDS   (256U)
 Stack size (in stk_word_t units) for the internal reaper task that reclaims detached-thread resources (default: 256 words).
#define STK_C_PTHREAD_KEYS_MAX   (8U)
 Maximum number of concurrently-alive pthread_key_t's (default: 8).
#define PTHREAD_CREATE_JOINABLE   (0)
#define PTHREAD_CREATE_DETACHED   (1)
#define PTHREAD_CREATE_USER_NP   (0)
#define PTHREAD_CREATE_PRIVILEGED_NP   (1)
#define PTHREAD_MUTEX_NORMAL   (0)
#define PTHREAD_MUTEX_DEFAULT   (PTHREAD_MUTEX_NORMAL)
#define PTHREAD_MUTEX_ERRORCHECK   (1)
#define PTHREAD_MUTEX_RECURSIVE   (2)
#define PTHREAD_MUTEX_INITIALIZER   {0}
#define PTHREAD_COND_INITIALIZER   {0}
#define PTHREAD_RWLOCK_INITIALIZER   {0}
#define PTHREAD_PROCESS_PRIVATE   (0)
#define PTHREAD_PROCESS_SHARED   (1)
#define PTHREAD_BARRIER_SERIAL_THREAD   (-1)
 Returned by pthread_barrier_wait() to exactly one arbitrary caller per round; all others receive 0. See "pthread_barrier_wait() note" in the file-level docs.
#define PTHREAD_ONCE_INIT   {0}
#define PTHREAD_KEYS_MAX   (STK_C_PTHREAD_KEYS_MAX)
 POSIX-standard name for STK_C_PTHREAD_KEYS_MAX (this file).
#define PTHREAD_DESTRUCTOR_ITERATIONS   (4)
 Maximum number of passes over a finishing thread's keys made while destructors keep setting new non-NULL values for their own key. See "pthread_key_t / thread-specific data" in the file-level docs.

Typedefs

typedef struct pthread_stk_ctrl_t * pthread_t
 Opaque thread handle.
typedef struct pthread_attr_t pthread_attr_t
 Thread creation attributes.
typedef struct pthread_mutexattr_t pthread_mutexattr_t
 Mutex attributes.
typedef struct pthread_mutex_t pthread_mutex_t
 A pthread mutex.
typedef struct pthread_condattr_t pthread_condattr_t
 Condition variable attributes (currently no settable properties).
typedef struct pthread_cond_t pthread_cond_t
 A pthread condition variable.
typedef struct pthread_rwlockattr_t pthread_rwlockattr_t
 Read-write lock attributes (currently no settable properties).
typedef struct pthread_rwlock_t pthread_rwlock_t
 A pthread read-write lock.
typedef struct pthread_spinlock_t pthread_spinlock_t
 A pthread spinlock.
typedef struct pthread_barrierattr_t pthread_barrierattr_t
 Barrier attributes (currently no settable properties).
typedef struct pthread_barrier_t pthread_barrier_t
 A pthread barrier.
typedef struct pthread_once_t pthread_once_t
 A pthread_once() control object.
typedef unsigned int pthread_key_t
 A thread-specific data key.

Functions

void stk_pthread_bind_kernel (stk_kernel_t *kernel)
 Bind the STK kernel instance that pthread_create() will add new threads to.
int pthread_attr_init (pthread_attr_t *attr)
 Initialize a thread attributes object with default values (default stack size, no external stack, joinable).
int pthread_attr_destroy (pthread_attr_t *attr)
 Destroy a thread attributes object (no-op; no owned resources).
int pthread_attr_setstacksize (pthread_attr_t *attr, size_t stacksize)
 Set the requested stack size in bytes.
int pthread_attr_getstacksize (const pthread_attr_t *attr, size_t *stacksize)
 Get the stack size in bytes that threads created with attr will use.
int pthread_attr_setstack (pthread_attr_t *attr, void *stackaddr, size_t stacksize)
 Supply an external, caller-owned stack buffer for the thread.
int pthread_attr_getstack (const pthread_attr_t *attr, void **stackaddr, size_t *stacksize)
 Get the previously-set external stack (stackaddr is NULL if none set).
int pthread_attr_setdetachstate (pthread_attr_t *attr, int detachstate)
 Set PTHREAD_CREATE_JOINABLE or PTHREAD_CREATE_DETACHED.
int pthread_attr_getdetachstate (const pthread_attr_t *attr, int *detachstate)
 Get the current detach-state setting.
int pthread_attr_setprivileged_np (pthread_attr_t *attr, int mode)
 Non-portable: select the STK access mode of threads created with attr.
int pthread_attr_getprivileged_np (const pthread_attr_t *attr, int *mode)
 Non-portable: get the access mode set in attr.
int pthread_create (pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
 Create and start a new thread.
int pthread_join (pthread_t thread, void **retval)
 Block until the given joinable thread finishes, then reclaim its resources.
int pthread_detach (pthread_t thread)
 Mark a thread as detached.
void pthread_exit (void *retval)
 Terminate the calling thread.
pthread_t pthread_self (void)
 Return the calling thread's own handle.
int pthread_equal (pthread_t t1, pthread_t t2)
 Compare two thread handles for equality.
int pthread_yield (void)
 Voluntarily give up the CPU to another ready task (cooperative yield), then resume once rescheduled.
int pthread_mutexattr_init (pthread_mutexattr_t *attr)
 Initialize a mutex attributes object with default values (PTHREAD_MUTEX_DEFAULT).
int pthread_mutexattr_destroy (pthread_mutexattr_t *attr)
 Destroy a mutex attributes object (no-op; no owned resources).
int pthread_mutexattr_settype (pthread_mutexattr_t *attr, int type)
 Set the mutex type.
int pthread_mutexattr_gettype (const pthread_mutexattr_t *attr, int *type)
 Get the mutex type stored in attr.
int pthread_mutex_init (pthread_mutex_t *mutex, const pthread_mutexattr_t *attr)
 Initialize a mutex.
int pthread_mutex_destroy (pthread_mutex_t *mutex)
 Destroy a mutex, releasing the underlying STK mutex object.
int pthread_mutex_lock (pthread_mutex_t *mutex)
 Lock a mutex, blocking until it becomes available.
int pthread_mutex_trylock (pthread_mutex_t *mutex)
 Try to lock a mutex without blocking.
int pthread_mutex_unlock (pthread_mutex_t *mutex)
 Unlock a mutex held by the calling thread.
int pthread_mutex_timedlock (pthread_mutex_t *mutex, const struct timespec *abstime)
 Lock with an absolute deadline.
int pthread_condattr_init (pthread_condattr_t *attr)
 Initialize a condition variable attributes object (no settable properties).
int pthread_condattr_destroy (pthread_condattr_t *attr)
 Destroy a condition variable attributes object (no-op).
int pthread_cond_init (pthread_cond_t *cond, const pthread_condattr_t *attr)
 Initialize a condition variable.
int pthread_cond_destroy (pthread_cond_t *cond)
 Destroy a condition variable, releasing the underlying STK object.
int pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex)
 Atomically unlock mutex and wait for a signal; re-locks mutex before returning.
int pthread_cond_timedwait (pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)
 As pthread_cond_wait(), with an absolute deadline.
int pthread_cond_signal (pthread_cond_t *cond)
 Wake one thread waiting on cond (no effect if none is waiting).
int pthread_cond_broadcast (pthread_cond_t *cond)
 Wake all threads waiting on cond (no effect if none is waiting).
int pthread_rwlockattr_init (pthread_rwlockattr_t *attr)
 Initialize a read-write lock attributes object (no settable properties).
int pthread_rwlockattr_destroy (pthread_rwlockattr_t *attr)
 Destroy a read-write lock attributes object (no-op).
int pthread_rwlock_init (pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr)
 Initialize a read-write lock.
int pthread_rwlock_destroy (pthread_rwlock_t *rwlock)
 Destroy a read-write lock, releasing the underlying STK object.
int pthread_rwlock_rdlock (pthread_rwlock_t *rwlock)
 Acquire the lock for shared reading. Blocks until available.
int pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock)
 Try to acquire the read lock without blocking.
int pthread_rwlock_timedrdlock (pthread_rwlock_t *rwlock, const struct timespec *abstime)
 Acquire the read lock with an absolute deadline.
int pthread_rwlock_wrlock (pthread_rwlock_t *rwlock)
 Acquire the lock for exclusive writing. Blocks until available.
int pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock)
 Try to acquire the write lock without blocking.
int pthread_rwlock_timedwrlock (pthread_rwlock_t *rwlock, const struct timespec *abstime)
 Acquire the write lock with an absolute deadline.
int pthread_rwlock_unlock (pthread_rwlock_t *rwlock)
 Release a read or write hold, whichever the calling thread holds.
int pthread_spin_init (pthread_spinlock_t *lock, int pshared)
 Initialize a spinlock.
int pthread_spin_destroy (pthread_spinlock_t *lock)
 Destroy a spinlock.
int pthread_spin_lock (pthread_spinlock_t *lock)
 Acquire the spinlock, spinning until available.
int pthread_spin_trylock (pthread_spinlock_t *lock)
 Try to acquire the spinlock without blocking.
int pthread_spin_unlock (pthread_spinlock_t *lock)
 Release a spinlock held by the calling thread.
int pthread_barrierattr_init (pthread_barrierattr_t *attr)
 Initialize a barrier attributes object (no settable properties).
int pthread_barrierattr_destroy (pthread_barrierattr_t *attr)
 Destroy a barrier attributes object (no-op).
int pthread_barrier_init (pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count)
 Initialize a barrier for count participating threads.
int pthread_barrier_destroy (pthread_barrier_t *barrier)
 Destroy a barrier, releasing the underlying STK barrier object.
int pthread_barrier_wait (pthread_barrier_t *barrier)
 Block until count threads have called this function, then release them all together; the barrier resets for reuse.
int pthread_once (pthread_once_t *once_control, void(*init_routine)(void))
 Call init_routine exactly once for a given once_control, no matter how many threads call pthread_once() on it concurrently.
int pthread_key_create (pthread_key_t *key, void(*destructor)(void *))
 Allocate a new thread-specific data key.
int pthread_key_delete (pthread_key_t key)
 Free a thread-specific data key.
int pthread_setspecific (pthread_key_t key, const void *value)
 Set the calling thread's value for key.
void * pthread_getspecific (pthread_key_t key)
 Get the calling thread's value for key.

Detailed Description

A minimal, POSIX-named pthreads-style API for STK. The public interface needs only stk_c.h / stk_c_memory.h (no C++ headers), so it can be used from C. The implementation (stk_c_pthread.cpp) calls the STK C++ API directly for thread/task control, TLS, critical sections, sleep/yield/time and the synchronization primitives; the stk_*_mem_t blobs embedded in the pthread_* types are used as in-place storage for those C++ objects.

Covers thread lifecycle and attributes, mutexes, condition variables, read-write locks, spinlocks, barriers, pthread_once() and thread-specific data. This is deliberately a subset of full POSIX threads, scoped to what maps cleanly onto STK's task/synchronization model:

POSIX area Support
pthread_create/join/detach/exit/self/equal Yes
pthread_yield (non-POSIX; POSIX uses sched_yield()) Yes (GNU/BSD-style extension)
pthread_attr_t (stacksize, stack, detachstate) Yes
pthread_attr_set/getprivileged_np Yes (non-portable STK extension)
pthread_mutex_t (normal/default) Yes (recursive in practice, see PTHREAD_MUTEX_NORMAL note)
pthread_mutex_t (recursive, errorcheck) Yes
pthread_cond_t (wait/timedwait/signal/broadcast) Yes
pthread_rwlock_t (rdlock/wrlock/tryrdlock/trywrlock/timed variants/unlock) Yes
pthread_spin_* (init/destroy/lock/trylock/unlock) Yes
pthread_barrier_t (init/destroy/wait) Yes
pthread_once Yes
pthread_key_t (create/delete/setspecific/getspecific, destructors) Yes
stk_pthread_bind_kernel() Yes (STK-specific bootstrap, not POSIX; required before pthread_create())
Cancellation (pthread_cancel etc.) No
Thread scheduling policy / priority get-set No (pthread_t is opaque, so stk_task_set_priority() cannot be applied to it)
Requirements on the STK kernel configuration
  • The kernel type configured for the bound core (STK_C_KERNEL_TYPE_CPU_X in stk_config.h, X being the core number passed to stk_kernel_create()) must include KERNEL_DYNAMIC (threads finish by returning from their entry function or calling pthread_exit(), which STK only supports for dynamic kernels) and KERNEL_SYNC (which enables the Mutex/Event/Semaphore primitives this shim is built on). The shim cannot verify this: a mismatch is a build configuration error and is not reported through an error code.
  • STK_C_KERNEL_MAX_TASKS must be large enough to hold: the application's own pre-existing tasks + one internal reaper task (added to the kernel by stk_pthread_bind_kernel(), whether or not any thread is ever detached) + the maximum number of concurrently-alive pthreads (joined-but-not-yet-collected and detached-but-not-yet-reaped threads still count as "alive" until reclaimed).
Memory
A thread created with the default stack size (STK_C_PTHREAD_DEFAULT_STACK_WORDS) takes its stack from a static pool and uses no heap. Any other size set via pthread_attr_setstacksize() is allocated with malloc() and freed when the thread is reclaimed. A stack supplied through pthread_attr_setstack() is used as-is and is never freed by the shim. The internal reaper task uses a static stack of STK_C_PTHREAD_REAPER_STACK_WORDS words.
  • STK_C_PTHREAD_MAX_THREADS (this file) bounds how many pthread_t's can exist concurrently; increase it via a -D define or in stk_config.h.
Bootstrapping
This shim never creates or starts a kernel itself. The application must create and start its own STK kernel exactly as it would without pthreads, and then call stk_pthread_bind_kernel() once (before the first pthread_create() call) so the shim knows which kernel to add new threads to (the call also adds the internal reaper task to that kernel):
stk_task_t *main_task = stk_task_create_user(MainEntry, NULL, main_stack, MAIN_STACK_WORDS);
stk_kernel_add_task(k, main_task);
stk_kernel_start(k); // never returns; MainEntry() may now call pthread_create()
stk_task_t * stk_task_create_user(stk_task_entry_t entry, void *arg, stk_word_t *stack, uint32_t stack_size)
Create user-mode task.
Definition stk_c.cpp:567
struct stk_kernel_t stk_kernel_t
Opaque handle to a kernel instance.
Definition stk_c.h:131
void stk_kernel_start(stk_kernel_t *k)
Start the scheduler - never returns.
Definition stk_c.cpp:406
void stk_kernel_add_task(stk_kernel_t *k, stk_task_t *tsk)
Add task to non-HRT kernel (static or dynamic).
Definition stk_c.cpp:428
#define STK_PERIODICITY_DEFAULT
Default tick period (1 ms).
Definition stk_c.h:139
void stk_kernel_init(stk_kernel_t *k, uint32_t tick_period_us)
Initialize kernel with given tick period.
Definition stk_c.cpp:399
stk_kernel_t * stk_kernel_create(uint8_t core_nr)
Create kernel.
Definition stk_c.cpp:331
void stk_pthread_bind_kernel(stk_kernel_t *kernel)
Bind the STK kernel instance that pthread_create() will add new threads to.
pthread_self() and foreign tasks (adoption)
pthread_self() is backed by STK's per-task TLS slot (stk_tls_get()), which this shim populates for every thread it creates via pthread_create(). A task the shim did not create - typically the application's bootstrap task added to the kernel before stk_kernel_start() - is adopted on first use: the first call to pthread_self() or pthread_setspecific() from such a task gives it a pthread_t and stores that handle in its TLS slot. Consequences:
  • each adopted task permanently consumes one STK_C_PTHREAD_MAX_THREADS slot, so size that limit as (pthread_create()'d threads + adopted tasks);
  • an adopted thread is not joinable and never reaped (pthread_join() and pthread_detach() return EINVAL for it);
  • the task's TLS slot now belongs to this shim: the application must not call stk_tls_set() in a task that uses the pthread API (it would replace the handle);
  • pthread_exit() from an adopted task runs its key destructors and then parks the task forever (the shim holds no kernel task object for it, so it cannot ask the kernel to remove it);
  • pthread_getspecific() never adopts (it returns NULL for a task that has not yet stored any value), so merely reading does not consume a slot. pthread_self() returns NULL only if no slot is left to adopt into.
Context restrictions
The blocking calls - pthread_join(), the lock/wait functions of mutexes, condition variables and read-write locks (including the timed variants, which block until the deadline), pthread_barrier_wait() and pthread_once() - must not be called from an ISR: the underlying STK primitives are ISR-unsafe for blocking operations. STK's non-blocking operations (try-lock, unlock, notify) are ISR-safe, but the shim adds lazy initialization and bookkeeping around them, so treat the pthread API as task-context only.
pthread_cond_timedwait() / pthread_mutex_timedlock() limitation
STK has no wall-clock (CLOCK_REALTIME) concept - only a monotonic tick count since stk_kernel_init(). abstime is therefore a point on the timeline of stk::GetTimeNowMs() (C alias: stk_time_now_ms()): derive it by adding a duration to a value read from that function, never from a time-of-day clock:
stk_time_t deadline_ms = stk_time_now_ms() + 500; // 500 ms from now
struct timespec abstime;
abstime.tv_sec = (time_t)(deadline_ms / 1000);
abstime.tv_nsec = (long)((deadline_ms % 1000) * 1000000);
int rc = pthread_cond_timedwait(&cond, &mutex, &abstime); // 0 or ETIMEDOUT
int64_t stk_time_t
Time value.
Definition stk_c.h:113
stk_time_t stk_time_now_ms(void)
Returns current time in milliseconds since kernel start.
Definition stk_c.cpp:636
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)
As pthread_cond_wait(), with an absolute deadline.

abstime is converted to a relative timeout in milliseconds (and then to kernel ticks). A deadline that is already in the past yields a zero timeout, so the call behaves like a non-blocking attempt, and very distant deadlines are clamped to INT32_MAX ms. The same interpretation applies to pthread_rwlock_timedrdlock() and pthread_rwlock_timedwrlock().

pthread_rwlock_unlock() disambiguation
POSIX uses a single pthread_rwlock_unlock() call to release either a read or a write hold, but the underlying stk::sync::RWMutex exposes separate ReadUnlock() / Unlock() calls. This shim disambiguates by recording, inside the pthread_rwlock_t itself, whether the lock is currently held for writing; this is safe because a write hold is always exclusive (no reader can be active while it is set), so the flag unambiguously identifies which underlying call to make. As with real pthreads, calling pthread_rwlock_unlock() on a lock you do not hold is undefined behavior and is not detected here.
pthread_spin_* note
Spinlocks busy-wait and are not recursive: relocking from the owning thread deadlocks. Only PTHREAD_PROCESS_PRIVATE is supported for the pshared argument to pthread_spin_init(); PTHREAD_PROCESS_SHARED returns ENOTSUP (STK has no cross-process concept).
pthread_barrier_wait() note
Like the underlying stk::sync::Barrier::Wait(), pthread_barrier_wait() is ISR-unsafe - do not call it from interrupt context. It returns PTHREAD_BARRIER_SERIAL_THREAD to exactly one arbitrary calling thread per round (the one whose arrival trips the barrier) and 0 to the rest, matching POSIX.
pthread_once() note
Each pthread_once_t lazily creates its own guard mutex (on first use; it is embedded in the pthread_once_t and never explicitly destroyed) and blocks concurrent callers on it while init_routine runs, so - unlike a naive flag check - a thread that calls pthread_once() while another thread's init_routine is still running correctly waits for it to finish rather than racing ahead. init_routine must not itself call pthread_once() on the same pthread_once_t (self-deadlock, as in real POSIX).
pthread_key_t / thread-specific data
Per-thread values set via pthread_setspecific() are stored in the thread's control block (reached through STK's per-task TLS slot). Foreign tasks are adopted on first pthread_setspecific() as described above (ENOMEM if no slot is left). Destructors (if any) run when a thread finishes (natural return or pthread_exit()), for at most PTHREAD_DESTRUCTOR_ITERATIONS passes - matching POSIX, a destructor that calls pthread_setspecific() to set a new non-NULL value for its own key causes another pass. They do not run for an adopted task that simply never exits. STK_C_PTHREAD_KEYS_MAX (this file) bounds how many keys can exist concurrently.

Definition in file stk_c_pthread.h.

Macro Definition Documentation

◆ _SYS__PTHREADTYPES_H_

#define _SYS__PTHREADTYPES_H_

Definition at line 15 of file stk_c_pthread.h.