Part III: Kernel Services
POSIX Thread
POSIX Threads (pthread_t) represent a schedulable execution unit.
Each thread executes independently with its own stack and priority, managed by the scheduler.
Threads can be used by including pthread.h.
Threads are always enabled and the kernel must be configured with configuration option QRT_CFG_THREAD_LIMIT with non-zero value.
Thread Attributes
Threads are created with the default attributes if no attributes are specified. The default attributes are defined as:
Detach state:
PTHREAD_CREATE_JOINABLECancellation state:
PTHREAD_CANCEL_ENABLECancellation type:
PTHREAD_CANCEL_DEFERREDStack size:
QRT_CFG_THREAD_STACK_DEFAULTScheduling policy:
SCHED_RRScheduling priority: lowest priority (1)
Timeslice:
QRT_CFG_TIME_SLICE_MSPrivilege level:
PTHREAD_UNPRIVILEGED_NP
To customize thread attributes before passing it to pthread_create() and a pthread_attr_t object must be initialized with pthread_attr_init().
The pthread_attr_t object can then be modified with:
Stack size (if stack not set):
pthread_attr_setstacksize()/pthread_attr_getstacksize()Scheduling policy:
pthread_attr_setschedpolicy()/pthread_attr_getschedpolicy()Scheduling priority:
pthread_attr_setschedparam()/pthread_attr_setschedparam()Detach state:
pthread_attr_setdetachstate()/pthread_attr_getdetachstate()Scheduling attribute inheritance:
pthread_attr_setinheritsched()/pthread_attr_getinheritsched()Uninitialize the attribute object:
pthread_attr_destroy()
QuantumRT also provides extensions to configure aditional thread attributes:
Timeslice:
pthread_attr_settimeslice_np()/pthread_attr_gettimeslice_np()Privilege level:
pthread_attr_setprivilege_np()/pthread_attr_getprivilege_np()Thread name:
pthread_attr_setname_np()/pthread_attr_getname_np()
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().
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.
Thread Lifecycle
Threads are created using pthread_create() and are exited with pthread_exit().
Cancelling a thread with pthread_cancel() allows it to be terminated from another thread.
Both deferred and asynchronous cancellation are supported:
Deferred cancellation allows a thread to specify cancellation points where it can be safely cancelled. Cancel points include blocking system calls and explicit cancellation points with
pthread_testcancel().Asynchronous cancellation cancels a thread immediately.
Threads can be joined with (pthread_join()) allowing another thread to wait for its termination and retrieve its exit status.
Joined threads resources are automatically released upon termination.
Only deferred cancelability type threads can be joined.
Detaching a thread with pthread_detach() allows it to run independently.
Detached threads resources are automatically released upon termination.
Cleanup handlers are used to ensure resources are released when a thread is cancelled or exits.
Cleanup handlers can be registered with pthread_cleanup_push() and pthread_cleanup_pop().
QRT_CFG_CLEANUP_LIMIT is used to configure the number of cleanup handlers per thread.
POSIX Semaphore
POSIX semaphores provide a mechanism for signaling between threads and managing access to shared resources.
Semaphores can be used by including semaphore.h.
Semaphores can be created using sem_init() and destroyed with sem_destroy().
Threads can wait on a semaphore using sem_trywait() / sem_wait() / sem_timedwait() / sem_clockwait() 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 used by including pthread.h.
Mutexes can be created using pthread_mutex_init() and destroyed with pthread_mutex_destroy().
Mutexes are initialized with default attributes if no attributes are specified. The default attributes are defined as:
Protocol:
PTHREAD_PRIO_NONEPriority ceiling: 0
Type:
PTHREAD_MUTEX_DEFAULT
To customize mutex attributes before passing it to pthread_mutex_init() and a pthread_mutexattr_t object must be initialized with pthread_mutexattr_init().
The pthread_mutexattr_t object can then be modified with:
Protocol:
pthread_mutexattr_setprotocol()/pthread_mutexattr_getprotocol()Priority ceiling:
pthread_mutexattr_setprioceiling()/pthread_mutexattr_getprioceiling()Type:
pthread_mutexattr_settype()/pthread_mutexattr_gettype()Uninitialize the attribute object:
pthread_mutexattr_destroy()
Threads can lock a mutex using pthread_mutex_trylock() / pthread_mutex_lock() / pthread_mutex_timedlock() / pthread_mutex_clocklock() and unlock a mutex using pthread_mutex_unlock().
Mutexes can be enabled by setting configuration option QRT_CFG_MUTEX_ENABLE.
Each thread can hold a limited number of mutexes simultaneously, configured with QRT_CFG_MUTEX_LIST_SIZE.
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 one of the following values:
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.Use timed lock mechanisms (
pthread_mutex_timedlock()) to limit the wait time for acquiring a lock.
Note
The kernel does not provide built-in deadlock detection or prevention.
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.
Threads can be used by including mqueue.h.
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 queue attributes can be configured with mq_setattr() and retrieved with mq_getattr().
Default attributes are used if no attributes are specified during creation.
The default attributes are defined as:
mq_maxmsg: 10
mq_msgsize: 256 bytes
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.
Threads can be used by including time.h.
Timers can be created using timer_create() and deleted with timer_delete().
Timers can be started with timer_settime() and stopped with timer_settime() by setting zero time.
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.
POSIX Clock
POSIX Clocks provide a mechanism for retrieving and manipulating time values.
Threads can be used by including time.h.
The kernel supports multiple clock sources, including:
CLOCK_REALTIME: System-wide real-time clock.CLOCK_MONOTONIC: Monotonic clock that cannot be set and represents elapsed time since an unspecified starting point.
Clock API provides functions for retrieving the current time with clock_gettime(), setting the time with clock_settime().
System Call Extension
System Call Extensions are designed for invoking custom system calls in kernel context when memory protection is enabled. They enable unprivileged threads to access peripheral devices.
Privileged threads can register System Call Extensions with qrt_syscall_register().
System Call Extensions are 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 enabling QRT_CFG_DEFER_ENABLE and by setting configuration option QRT_CFG_DEFER_LIMIT with non-zero value.