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:

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:

Comparison between semaphore and mutex

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.