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 implemented on top of the STK C bindings (stk_c.h / stk_c_memory.h) only - no other STK headers are required. More...

#include "stk_c.h"
#include "stk_c_memory.h"
#include <ctime>
#include <cerrno>
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 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_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 limitation" in the file-level docs.

Typedefs

typedef struct pthread_stk_ctrl_tpthread_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 currently requested stack size in bytes (0 = default).
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 (NULL/0 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_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)
int pthread_mutexattr_destroy (pthread_mutexattr_t *attr)
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)
int pthread_mutex_init (pthread_mutex_t *mutex, const pthread_mutexattr_t *attr)
 Initialize a mutex.
int pthread_mutex_destroy (pthread_mutex_t *mutex)
int pthread_mutex_lock (pthread_mutex_t *mutex)
int pthread_mutex_trylock (pthread_mutex_t *mutex)
int pthread_mutex_unlock (pthread_mutex_t *mutex)
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)
int pthread_condattr_destroy (pthread_condattr_t *attr)
int pthread_cond_init (pthread_cond_t *cond, const pthread_condattr_t *attr)
int pthread_cond_destroy (pthread_cond_t *cond)
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)
int pthread_cond_broadcast (pthread_cond_t *cond)
int pthread_rwlockattr_init (pthread_rwlockattr_t *attr)
int pthread_rwlockattr_destroy (pthread_rwlockattr_t *attr)
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)
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)
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)
int pthread_barrierattr_init (pthread_barrierattr_t *attr)
int pthread_barrierattr_destroy (pthread_barrierattr_t *attr)
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)
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 implemented on top of the STK C bindings (stk_c.h / stk_c_memory.h) only - no other STK headers are required.

Covers thread lifecycle (create/join/detach/exit/self/equal), mutexes and condition variables. 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/yield Yes
pthread_attr_t (stacksize, stack, detachstate) Yes
pthread_mutex_t (normal, non-recursive) Yes
pthread_mutex_t (recursive, errorcheck) No (returns ENOTSUP)
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
Cancellation (pthread_cancel etc.) No
Thread scheduling policy / priority get-set No (use stk_task_set_priority() directly via stk_task_get_instance-less path if needed)
Requirements on the STK kernel configuration
  • The bound kernel must be KERNEL_DYNAMIC (threads finish by returning from their entry function or calling pthread_exit(), which STK only supports for dynamic kernels).
  • STK_C_KERNEL_MAX_TASKS must be large enough to hold: the application's own pre-existing tasks + one internal reaper task (created lazily on first use) + 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).
  • 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:
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:568
struct stk_kernel_t stk_kernel_t
Opaque handle to a kernel instance.
Definition stk_c.h:126
void stk_kernel_start(stk_kernel_t *k)
Start the scheduler - never returns.
Definition stk_c.cpp:407
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:429
#define STK_PERIODICITY_DEFAULT
Default tick period (1 ms).
Definition stk_c.h:134
void stk_kernel_init(stk_kernel_t *k, uint32_t tick_period_us)
Initialize kernel with given tick period.
Definition stk_c.cpp:400
stk_kernel_t * stk_kernel_create(uint8_t core_nr)
Create kernel.
Definition stk_c.cpp:332
void stk_pthread_bind_kernel(stk_kernel_t *kernel)
Bind the STK kernel instance that pthread_create() will add new threads to.
pthread_self() limitation
pthread_self() is backed by STK's per-task TLS slot (stk_tls_get()), which this shim populates only for threads it created via pthread_create(). Calling pthread_self() from the application's original bootstrap task (the one added to the kernel before stk_kernel_start()) returns NULL, since that task was never handed a pthread_t. If the bootstrap task needs a pthread_t identity, create it via pthread_create() instead of stk_task_create_user() + manual stk_kernel_add_task().
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 interpreted as a point on the same timeline as stk_time_now_ms() (i.e. derive it by adding a duration to a value you obtained from stk_time_now_ms(), not from a real-time-of-day clock). 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_rwmutex_t exposes separate stk_rwmutex_read_unlock() / stk_rwmutex_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_* recursion note
The underlying stk_spinlock_t is recursive (the owning thread/ISR may re-acquire it without deadlocking). Real POSIX spinlocks are non-recursive, and a thread that relocks one it already holds has undefined behavior; this shim's spinlocks are therefore a strict superset - portable code written against real pthread_spin_* semantics will still work correctly here. 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_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) 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 limitation
Like pthread_self(), per-thread values set via pthread_setspecific() are stored in STK's per-task TLS slot and are therefore only available to threads created via pthread_create(); calling pthread_setspecific() from the application's original bootstrap task returns EINVAL, and pthread_getspecific() returns NULL for it. Destructors (if any) run once, for at most PTHREAD_DESTRUCTOR_ITERATIONS passes, when a pthread_create()'d thread finishes (natural return or pthread_exit()) - matching POSIX, a destructor that calls pthread_setspecific() to set a new non-NULL value for its own key causes another pass. STK_C_PTHREAD_KEYS_MAX (this file) bounds how many keys can exist concurrently.

Definition in file stk_c_pthread.h.