When building an application (realtime, embedded, graphical, or otherwise), the developer may want several algorithms within the application to execute concurrently. This concurrency is achieved by using the POSIX thread model, which defines a process as containing one or more threads of execution. A thread can be thought of as the minimum unit of execution, the unit of scheduling and execution in the QNX Neutrino microkernel. A process, on the other hand, can be thought of as a container for threads, defining the address space within which threads execute. A process always contains at least one thread.
Depending on the nature of the application, threads might execute independently with no need to communicate between the algorithms (unlikely), or they may need to be tightly coupled, with high-bandwidth communications and tight synchronization. To assist in this communication and synchronization, the BlackBerry 10 OS provides a rich variety of IPC and synchronization services.
The following pthread_* (POSIX Threads) library calls don't involve any microkernel thread calls:
The following table lists the POSIX thread calls that have a corresponding microkernel thread call, allowing you to choose either interface:
| POSIX call | Microkernel call | Description |
|---|---|---|
| pthread_create() | ThreadCreate() | Create a new thread of execution |
| pthread_exit() | ThreadDestroy() | Destroy a thread |
| pthread_detach() | ThreadDetach() | Detach a thread so it doesn't need to be joined |
| pthread_join() | ThreadJoin() | Join a thread waiting for its exit status |
| pthread_cancel() | ThreadCancel() | Cancel a thread at the next cancellation point |
| N/A | ThreadCtl() | Change a thread's BlackBerry 10 OS-specific thread characteristics |
| pthread_mutex_init() | SyncTypeCreate() | Create a mutex |
| pthread_mutex_destroy() | SyncDestroy() | Destroy a mutex |
| pthread_mutex_lock() | SyncMutexLock() | Lock a mutex |
| pthread_mutex_trylock() | SyncMutexLock() | Conditionally lock a mutex |
| pthread_mutex_unlock() | SyncMutexUnlock() | Unlock a mutex |
| pthread_cond_init() | SyncTypeCreate() | Create a condition variable |
| pthread_cond_destroy() | SyncDestroy() | Destroy a condition variable |
| pthread_cond_wait() | SyncCondvarWait() | Wait on a condition variable |
| pthread_cond_signal() | SyncCondvarSignal() | Signal a condition variable |
| pthread_cond_broadcast() | SyncCondvarSignal() | Broadcast a condition variable |
| pthread_getschedparam() | SchedGet() | Get the scheduling parameters and policy of a thread |
| pthread_setschedparam() , pthread_setschedprio() | SchedSet() | Set the scheduling parameters and policy of a thread |
| pthread_sigmask() | SignalProcmask() | Examine or set a thread's signal mask |
| pthread_kill() | SignalKill() | Send a signal to a specific thread |
The OS can be configured to provide a mix of threads and processes (as defined by POSIX). Each process is MMU-protected from each other, and each process may contain one or more threads that share the process's address space.
The environment you choose affects not only the concurrency capabilities of the application, but also the IPC and synchronization services the application might make use of.
For information about processes and threads from the programming point of view, see Processes and threads and Processes.
Although threads within a process share everything within the process's address space, each thread still has some private data. In some cases, this private data is protected within the kernel (for example, the tid or thread ID), while other private data resides unprotected in the process's address space (for example, each thread has a stack for its own use). Some of the more noteworthy thread-private resources are:
For more information, see Thread scheduling.
Thread-specific data, implemented in the pthread library and stored in the TLS, provides a mechanism for associating a process global integer key with a unique per-thread data value. To use thread-specific data, you first create a new key and then bind a unique data value to the key (per thread). The data value may, for example, be an integer or a pointer to a dynamically allocated data structure. Subsequently, the key can return the bound data value per thread.
A typical application of thread-specific data is for a thread-safe function that needs to maintain a context for each calling thread.
You can use the following functions to create and manipulate this data:
| Function | Description |
|---|---|
| pthread_key_create() | Create a data key with destructor function |
| pthread_key_delete() | Destroy a data key |
| pthread_setspecific() | Bind a data value to a data key |
| pthread_getspecific() | Return the data value bound to a data key |
The number of threads within a process can vary widely, with threads being created and destroyed dynamically. Thread creation ( pthread_create() ) involves allocating and initializing the necessary resources within the process's address space (for example, thread stack) and starting the execution of the thread at some function in the address space.
Thread termination ( pthread_exit() , pthread_cancel() ) involves stopping the thread and reclaiming the thread's resources. As a thread executes, its state can generally be described as either ready or blocked. More specifically, it can be one of the following: