This library provides POSIX API compatibility for F9 microkernel applications, implementing the PSE51 (POSIX Minimal Realtime System Profile) as defined in IEEE Std 1003.13-2003, plus selected PSE52 (Realtime Controller) features.
PSE51 is designed for embedded hard real-time systems and includes:
- POSIX Threads (pthread) with condition variables
- Semaphores with notification-based blocking
- Mutexes with notification-based blocking and static initializers
- Thread scheduling (
SCHED_FIFO,SCHED_RR) - Clocks and timers (
clock_gettime,nanosleep) - Signal handling (
sigwait,pthread_sigmask)
PSE52 extensions implemented:
- Read-write locks (
pthread_rwlock_*) - Barriers (
pthread_barrier_*)
The implementation follows the seL4 ecosystem layering pattern:
┌────────────────────────────────────┐
│ POSIX Application Code │
├────────────────────────────────────┤
│ libposix (PSE51 API) │ ← This library
├────────────────────────────────────┤
│ L4 Syscalls (IPC, ThreadControl) │
├────────────────────────────────────┤
│ F9 Microkernel │
└────────────────────────────────────┘
-
Thread Management via Pager IPC
pthread_create()→pager_create_thread()→ L4 ThreadControl syscall- Threads share address space with creator (L4 shared AS model)
- Stack/UTCB allocated from pager's resource pool
pthread_join()uses pager IPC (last remaining pager sync handler)
-
Mutex Implementation (Notification-Based)
- ARM LDREX/STREX atomic operations for fast-path (uncontended case)
- Direct kernel notification blocking via
L4_NotifyWait(POSIX_NOTIFY_MUTEX_BIT) - TTAS spinlock pattern for waiter list protection
- Deadlock detection for normal mutexes
- Recursive mutex support (
PTHREAD_MUTEX_RECURSIVE) - Static initializer support (
PTHREAD_MUTEX_INITIALIZER) - Lazy initialization on first use (following posix-next pattern)
-
Semaphore Implementation (Notification-Based)
- Atomic counter using ARM exclusive load/store for fast-path
- Direct kernel notification blocking via
L4_NotifyWait(SEM_NOTIFY_BIT) - Waiter list with spinlock protection;
sem_post()wakes viaL4_NotifyPost() - Supports
sem_wait,sem_post,sem_trywait,sem_getvalue
-
Condition Variables (Sequence-Based Atomicity)
- Full
pthread_cond_*implementation with timed wait - Sequence counter (
signal_count,broadcast_seq) for lost-wakeup prevention pthread_cond_waitcaptures sequence before mutex release, preventing race- Polling-based timedwait using
L4_SystemClock(SYS_SYSTEM_CLOCK) - Proper signal/broadcast semantics with waiter list + notification
- Full
Thread Management:
pthread_create()- Create new threadpthread_exit()- Terminate calling threadpthread_join()- Wait for thread terminationpthread_detach()- Mark thread as detachedpthread_self()- Get current thread IDpthread_equal()- Compare thread IDs
Thread Cancellation:
pthread_cancel()- Request thread cancellationpthread_setcancelstate()- Set cancellation state (enable/disable)pthread_setcanceltype()- Set cancellation type (deferred only)pthread_testcancel()- Create cancellation point
Thread Attributes:
pthread_attr_init()- Initialize attributespthread_attr_destroy()- Destroy attributespthread_attr_setdetachstate()- Set detach statepthread_attr_getdetachstate()- Get detach statepthread_attr_setstacksize()- Set stack sizepthread_attr_getstacksize()- Get stack size
Mutexes:
pthread_mutex_init()- Initialize mutexpthread_mutex_destroy()- Destroy mutexpthread_mutex_lock()- Lock mutex (blocking)pthread_mutex_trylock()- Try to lock mutex (non-blocking)pthread_mutex_timedlock()- Lock mutex with timeoutpthread_mutex_unlock()- Unlock mutex
Mutex Attributes:
pthread_mutexattr_init()- Initialize mutex attributespthread_mutexattr_destroy()- Destroy mutex attributespthread_mutexattr_settype()- Set mutex type (normal/recursive)pthread_mutexattr_gettype()- Get mutex type
Condition Variables:
pthread_cond_init()- Initialize condition variablepthread_cond_destroy()- Destroy condition variablepthread_cond_wait()- Wait on condition (releases mutex atomically)pthread_cond_timedwait()- Wait with timeout (polling-based)pthread_cond_signal()- Wake one waiting threadpthread_cond_broadcast()- Wake all waiting threads
Condition Variable Attributes:
pthread_condattr_init()- Initialize condition attributespthread_condattr_destroy()- Destroy condition attributes
sem_init()- Initialize semaphoresem_destroy()- Destroy semaphoresem_wait()- Wait on semaphore (decrement, blocking)sem_trywait()- Try to wait on semaphore (non-blocking)sem_post()- Post to semaphore (increment)sem_getvalue()- Get current semaphore value
Clocks:
clock_gettime()- Get current time from specified clockclock_settime()- Set time on specified clockclock_getres()- Get clock resolution
Sleep:
nanosleep()- High-resolution sleep
Timers:
timer_create()- Create a timertimer_delete()- Delete a timertimer_settime()- Arm/disarm a timertimer_gettime()- Get remaining timetimer_getoverrun()- Get overrun count
Clock IDs: CLOCK_REALTIME, CLOCK_MONOTONIC
Signal Set Operations:
sigemptyset()- Initialize empty signal setsigfillset()- Initialize full signal setsigaddset()- Add signal to setsigdelset()- Remove signal from setsigismember()- Test signal membership
Signal Masking:
pthread_sigmask()- Examine/change thread signal masksigprocmask()- Process signal mask (single-threaded)
Signal Handling:
sigaction()- Examine/change signal actionsigwait()- Wait for signal from setsigpending()- Get pending signalspthread_kill()- Send signal to threadraise()- Send signal to self
Signals: SIGTERM, SIGKILL, SIGUSR1, SIGUSR2, SIGALRM, SIGINT
Priority Functions:
sched_get_priority_max()- Get maximum priority for policysched_get_priority_min()- Get minimum priority for policysched_yield()- Yield processor
Process Scheduling:
sched_getscheduler()- Get scheduling policysched_setscheduler()- Set scheduling policy and parameterssched_getparam()- Get scheduling parameterssched_setparam()- Set scheduling parameters
Thread Scheduling:
pthread_setschedparam()- Set thread scheduling policy/prioritypthread_getschedparam()- Get thread scheduling policy/prioritypthread_setschedprio()- Set thread prioritypthread_attr_setschedpolicy()- Set scheduling policy in attributespthread_attr_getschedpolicy()- Get scheduling policy from attributespthread_attr_setschedparam()- Set scheduling params in attributespthread_attr_getschedparam()- Get scheduling params from attributespthread_attr_setinheritsched()- Set inherit-scheduler attributepthread_attr_getinheritsched()- Get inherit-scheduler attribute
Policies: SCHED_FIFO, SCHED_RR, SCHED_OTHER
RW Lock Management:
pthread_rwlock_init()- Initialize read-write lockpthread_rwlock_destroy()- Destroy read-write lockpthread_rwlock_rdlock()- Acquire read lock (blocking)pthread_rwlock_tryrdlock()- Try to acquire read lock (non-blocking)pthread_rwlock_wrlock()- Acquire write lock (blocking)pthread_rwlock_trywrlock()- Try to acquire write lock (non-blocking)pthread_rwlock_unlock()- Release read or write lock
RW Lock Attributes:
pthread_rwlockattr_init()- Initialize attributespthread_rwlockattr_destroy()- Destroy attributes
Static initializer: PTHREAD_RWLOCK_INITIALIZER
Barrier Management:
pthread_barrier_init()- Initialize barrier with thread countpthread_barrier_destroy()- Destroy barrierpthread_barrier_wait()- Wait at barrier synchronization point
Barrier Attributes:
pthread_barrierattr_init()- Initialize attributespthread_barrierattr_destroy()- Destroy attributes
Return value: One thread receives PTHREAD_BARRIER_SERIAL_THREAD
pthread_t- Thread ID structurepthread_attr_t- Thread attributespthread_mutex_t- Mutex structurepthread_mutexattr_t- Mutex attributes (bitfield-packed)pthread_cond_t- Condition variable structurepthread_condattr_t- Condition variable attributes (bitfield-packed)pthread_rwlock_t- Read-write lock structure (PSE52)pthread_rwlockattr_t- RW lock attributes (PSE52)pthread_barrier_t- Barrier structure (PSE52)pthread_barrierattr_t- Barrier attributes (PSE52)sem_t- Semaphore structuresigset_t- Signal setstruct timespec- Time specificationstruct sched_param- Scheduling parametersclockid_t- Clock identifiertimer_t- Timer identifier
Static Initializers:
PTHREAD_MUTEX_INITIALIZER- Static mutex initializationPTHREAD_COND_INITIALIZER- Static condition variable initializationPTHREAD_RWLOCK_INITIALIZER- Static RW lock initialization
#include <libposix/pthread.h>
void *worker_thread(void *arg)
{
int id = *(int *)arg;
printf("Worker %d started\n", id);
return (void *)(id * 2);
}
int main(void)
{
pthread_t thread;
int thread_id = 42;
void *retval;
pthread_create(&thread, NULL, worker_thread, &thread_id);
pthread_join(&thread, &retval);
printf("Thread returned: %d\n", (int)retval);
return 0;
}#include <libposix/pthread.h>
pthread_mutex_t mutex;
int shared_counter = 0;
void *increment_thread(void *arg)
{
for (int i = 0; i < 1000; i++) {
pthread_mutex_lock(&mutex);
shared_counter++;
pthread_mutex_unlock(&mutex);
}
return NULL;
}
int main(void)
{
pthread_mutex_init(&mutex, NULL);
pthread_t threads[4];
for (int i = 0; i < 4; i++)
pthread_create(&threads[i], NULL, increment_thread, NULL);
for (int i = 0; i < 4; i++)
pthread_join(&threads[i], NULL);
printf("Final counter: %d\n", shared_counter); // Should be 4000
pthread_mutex_destroy(&mutex);
return 0;
}#include <libposix/semaphore.h>
#include <libposix/pthread.h>
sem_t sem;
int buffer;
void *producer(void *arg)
{
for (int i = 0; i < 10; i++) {
buffer = i;
sem_post(&sem);
}
return NULL;
}
void *consumer(void *arg)
{
for (int i = 0; i < 10; i++) {
sem_wait(&sem);
printf("Consumed: %d\n", buffer);
}
return NULL;
}
int main(void)
{
sem_init(&sem, 0, 0);
pthread_t prod, cons;
pthread_create(&prod, NULL, producer, NULL);
pthread_create(&cons, NULL, consumer, NULL);
pthread_join(&prod, NULL);
pthread_join(&cons, NULL);
sem_destroy(&sem);
return 0;
}#include <libposix/pthread.h>
/* Static initialization - no init() call needed */
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
int shared_data = 0;
void *reader(void *arg)
{
pthread_rwlock_rdlock(&rwlock);
/* Multiple readers can access concurrently */
printf("Read value: %d\n", shared_data);
pthread_rwlock_unlock(&rwlock);
return NULL;
}
void *writer(void *arg)
{
pthread_rwlock_wrlock(&rwlock);
/* Writers have exclusive access */
shared_data++;
pthread_rwlock_unlock(&rwlock);
return NULL;
}#include <libposix/pthread.h>
pthread_barrier_t barrier;
void *worker(void *arg)
{
int id = *(int *)arg;
printf("Thread %d: Phase 1 work\n", id);
/* All threads wait here until count reached */
int ret = pthread_barrier_wait(&barrier);
if (ret == PTHREAD_BARRIER_SERIAL_THREAD) {
printf("Thread %d: I'm the serial thread!\n", id);
}
printf("Thread %d: Phase 2 work\n", id);
return NULL;
}
int main(void)
{
pthread_barrier_init(&barrier, NULL, 4);
pthread_t threads[4];
int ids[4] = {0, 1, 2, 3};
for (int i = 0; i < 4; i++)
pthread_create(&threads[i], NULL, worker, &ids[i]);
for (int i = 0; i < 4; i++)
pthread_join(&threads[i], NULL);
pthread_barrier_destroy(&barrier);
return 0;
}Comprehensive PSE51 compliance tests are available in user/apps/posix/:
make config
# Enable POSIX tests in configuration
make
qemu-system-arm -M netduinoplus2 -nographic -serial mon:stdio -kernel build/netduinoplus2/f9.elfTest Results: 25 tests passing (17 pthread + 8 semaphore)
Test coverage includes:
- Thread creation, join, detach with return values
- Thread attributes (stack size, detach state)
- Thread cancellation (cancel, testcancel, setcancelstate)
- Mutex locking (normal, recursive, timed)
- Mutex error detection (deadlock, double-unlock)
- Condition variable wait/timedwait/signal/broadcast
- Spinlock init/destroy, lock/unlock, trylock, error cases
- Semaphore wait/post/trywait/getvalue
- Producer-consumer patterns
- Multi-threaded stress tests
- Notification-based blocking verification
- No Thread-Local Storage (TLS)
pthread_exit()stores return value via pager IPC (not true TLS)pthread_key_*APIs not implemented
- No Priority Inheritance
- Mutexes do not implement
PTHREAD_PRIO_INHERITprotocol pthread_mutexattr_*protocol/prioceilingreturnENOTSUP
- Mutexes do not implement
- Global Signal State (Not Per-Thread)
pending_signalsandthread_sigmaskare process-global- PSE51 requires per-thread signal delivery
- Fix would require kernel TLS support or per-TCB signal state
- Thread Cancellation Limitations
- Only
PTHREAD_CANCEL_DEFERREDsupported (asynchronous cancellation not safe for RT) - No cleanup handlers (
pthread_cleanup_push/popnot implemented) - Cancellation points limited to
pthread_testcancel()only
- Only
- Timed Wait Uses Relative Interpretation
pthread_cond_timedwait: Treats abstime as relative, adds to current time- True CLOCK_REALTIME absolute time would require epoch reference
sem_timedwait: Not yet implemented
- Stack Size Attribute Not Enforced
pthread_attr_setstacksize()accepted but not passed to pager- All threads use default stack size from pager configuration
| Category | Implemented | Missing/Stub |
|---|---|---|
| Threads | create, join, detach, exit, self, equal, attr_*, cancel, setcancelstate/type, testcancel | cleanup_push/pop |
| Mutexes | init, destroy, lock, trylock, timedlock, unlock | - |
| Mutex Attrs | init, destroy, settype, gettype | setprotocol, setprioceiling (ENOTSUP) |
| Condvars | init, destroy, wait, timedwait, signal, broadcast | - |
| Condvar Attrs | init, destroy | setclock (ENOTSUP) |
| Semaphores | init, destroy, wait, trywait, post, getvalue | timedwait |
| RW Locks | init, destroy, rdlock, wrlock, tryrd/wrlock, unlock | timedrdlock, timedwrlock |
| Barriers | init, destroy, wait | - |
| Spinlocks | init, destroy, lock, trylock, unlock | - |
| Signals | sigmask, sigaction, sigwait, sigpending, raise | Per-thread state (global only) |
| TLS | - | key_create, key_delete, getspecific, setspecific |
| One-time Init | - | pthread_once |
| Scheduling | setschedparam, getschedparam, setschedprio, yield, get_priority_min/max | - |
- IEEE Std 1003.13-2003 (PSE51/PSE52 Profiles)
- The Open Group Base Specifications Issue 7
- seL4 libsel4sync (semaphore/mutex reference implementation)
- Zephyr POSIX layer (externals/posix-next) - elastipool, bitfield patterns