Part III: Kernel Services
POSIX Thread
POSIX Threads (pthread_t) represent a schedulable execution unit in QuantumRT.
Each thread executes independently with its own stack and priority, managed by the scheduler.
The kernel must be configured with configuration option QRT_CFG_THREAD_LIMIT with non-zero value.
Thread Creation
Threads are created using pthread_create().
Thread attributes such as stack size, priority, and detach state can be configured using pthread_attr_t before creation.
Thread Lifecycle
Threads can be joined (pthread_join()), detached (pthread_detach()), or cancelled (pthread_cancel()).
QuantumRT supports both deferred and asynchronous cancellation.
Cleanup handlers can be registered with pthread_cleanup_push() and pthread_cleanup_pop() to ensure resources are released when a thread is cancelled or exits.
QRT_CFG_CLEANUP_LIMIT is used to configure the number of cleanup handlers per thread.
Thread Attributes
Attributes define initial parameters for a thread:
pthread_attr_init(), pthread_attr_setstacksize(), and related APIs control thread configuration.
Thread Stack Allocation
Threads require a dedicated stack for execution. The stack can be allocated either:
Statically with
pthread_attr_setstack()Dynamically with
pthread_attr_setstacksize(). Default behavior if stack is not set explicitly.
If the stack size is not explicitly set, a default size, which must be configured with QRT_CFG_THREAD_STACK_DEFAULT, is used.
If dynamic memory allocation is used, thread stack pool must be configured with sufficient memory with QRT_CFG_STACK_POOL_SIZE.
Note
ARMv6-M and ARMv7-M Stack base address must be stack size aligned.
ARMv8-M Stack base address must be 32-byte aligned if memory protection is enabled.
Note
Stack size must be power of two if memory protection is enabled.
Stack size must be power of two if dynamic allocation is used.
POSIX Semaphore
POSIX semaphores provide a mechanism for signaling between threads and managing access to shared resources.
They can be created using sem_init() and destroyed with sem_destroy().
Threads can wait on a semaphore using sem_trywait() / sem_wait() / sem_timedwait() and signal a semaphore using sem_post().
The current value of a semaphore can be queried with sem_getvalue()
Part of the semaphore API is available from deferred call context.
Note
Named semaphores are not supported.
Semaphores can be enabled by setting configuration option QRT_CFG_SEMAPHORE_ENABLE.
POSIX Thread Mutex
POSIX Thread Mutexes provide a mechanism for mutual exclusion, allowing threads to safely access shared resources without data corruption.
Mutexes can be created using pthread_mutex_init() and destroyed with pthread_mutex_destroy().
Mutex attributes can be configured using pthread_mutexattr_t before creation.
Threads can lock a mutex using pthread_mutex_trylock() / pthread_mutex_lock() / pthread_mutex_timedlock() and unlock a mutex using pthread_mutex_unlock().
Mutexes can be enabled by setting configuration option QRT_CFG_MUTEX_ENABLE.
Priority Inversion
POSIX Thread Mutexes support priority inheritance and priority ceiling protocols to mitigate priority inversion issues.
These protocols can be configured on mutex creation using pthread_mutexattr_setprotocol() with the values PTHREAD_PRIO_INHERIT, PTHREAD_PRIO_PROTECT or PTHREAD_PRIO_NONE.
Deadlocks
A deadlock occurs when multiple threads hold each other’s locks and these threads attempt to acquire each other’s locks with infinite timeout.
Recommended Practices to Avoid Deadlocks:
Always acquire multiple mutexes in a consistent global order across all threads.
Use try-lock mechanisms (
pthread_mutex_trylock()) to attempt acquiring locks without blocking indefinitely.Implement timeout mechanisms when acquiring locks to prevent indefinite waiting.
Note
The kernel does not provide built-in deadlock detection or prevention.
Semaphore vs. Mutex
Although semaphores and mutexes are both used for synchronization, they have key differences:
Feature |
Semaphore |
Mutex |
|---|---|---|
Ownership |
No ownership (any thread can signal) |
Has ownership (only the locking thread can release) |
Use Case |
Synchronization & multi-resource control |
Exclusive resource access |
Priority Inversion |
No built-in handling |
Supports priority inheritance and priority ceiling |
POSIX Message Queue
POSIX Message Queues provide a mechanism for threads to communicate by sending and receiving messages in a FIFO manner. Message queues support multiple producers and consumers, allowing threads to exchange data safely and efficiently.
A message queue is created using mq_open() and destroyed using mq_close() and mq_unlink().
Messages are sent to the queue using mq_send() / mq_timedsend() and received using mq_receive() / mq_timedreceive().
Message queues dynamically allocate memory from the kernel heap for messages.
The kernel heap must be configured with sufficient memory with QRT_CFG_KERNEL_HEAP_SIZE.
Message queues can be enabled by setting configuration option QRT_CFG_MESSAGE_QUEUE_ENABLE, and setting configuration option QRT_CFG_MESSAGE_QUEUE_LIMIT with non-zero value.
POSIX Timer
POSIX Timers provide a mechanism for executing callback functions at specified intervals or after a certain delay.
Timers can be created using timer_create() and deleted with timer_delete().
Timers can be started with timer_settime() and stopped with timer_delete().
When a timer expires, it invokes a user-defined callback function.
Software timers can be enabled by setting configuration option QRT_CFG_TIMER_ENABLE and setting configuration option QRT_CFG_TIMER_LIMIT with non-zero value.
System Call Extension
System Call Extensions are designed for invoking custom system calls in kernel context when memory protection is enabled.
System Call Extensions are registered with qrt_syscall_register() and invoked with qrt_syscall_invoke().
System Call Extensions can be enabled by setting configuration option QRT_CFG_SYSCALL_EXT_ENABLE and setting configuration option QRT_CFG_SYSCALL_EXT_LIMIT with non-zero value.
Deferred Call
QuantumRT supports deferred calls, allowing ISRs and callbacks to split workload between urgent and less urgent work.
Deferred calls are registered with qrt_defercall().
Deferred calls are executed in order they were pushed and only after all interrupt handlers have completed.
System calls are not directly available from ISR context. Instead, ISRs must use the deferred call to invoke system calls. Deferred call can safely invoke the following system calls:
Deferred Calls can be enabled by setting configuration option QRT_CFG_DEFER_LIMIT with non-zero value.