TI-POSIX User's Guide

Table of Contents

Overview

Select features of POSIX.1 and POSIX.4 are supported. These include thread, mutex, semaphore, read-write lock, barrier, key, message queue, clock, and timer. We provide a functional implementation of these features, but in some cases, not every aspect of the feature is supported.

Contributions to the POSIX implementation come from the compiler, the C run-time library, and the kernel. Integration with these contributors is provided by the TI-POSIX package.

In general terms, the compiler provides base type definitions, the C library provides language specific features, and TI-POSIX adds features which require kernel support. The following kernels are supported:

This user’s guide provides information on using POSIX with the kernels listed above. For detailed documentation on POSIX, please use the following links:

Building Your Program with TI-POSIX Support

There are three steps to using the TI-POSIX support in your program:

  1. Use POSIX in your source code
  2. Define your include path
  3. Link your program with TI-POSIX

The details of each step are given in the following sections.

Use POSIX in your source code

Include the POSIX header files in your source code using the file names defined by the Open Group Specification. For example, use the following to create a POSIX thread:

#include <pthread.h>

void *start_fxn(void *arg);
pthread_t thread;
int arg = 1;

pthread_create(&thread, NULL, start_fxn, (void *)&arg);

In previous versions of SYS/BIOS, the POSIX header files were included using a package-qualified path name. This is no longer supported. Do not use the following include statements:

#include <ti/sysbios/posix/pthread.h>       INCORRECT
#include <ti/sysbios/posix/sys/types.h>     INCORRECT

Use the following include statements:

#include <pthread.h>
#include <sys/types.h>

Define Your Include Path

The include path is used to select which TI-POSIX implementation you use at compile time. The include path must reflect which tool chain is in play. We currently support three vendors. Set your include path according to which compiler you are using.

Compiler Vendor Tag Include Path
Texas Instruments ccs /.../ti/posix/ccs
Texas Instruments (clang) ticlang /.../ti/posix/ticlang
GNU gcc /.../ti/posix/gcc
IAR Systems iar /.../ti/posix/iar

The compiler’s include directory is always added implicitly as the last entry on the include path. Thus, when an application includes a POSIX header file (e.g. #include <fcntl.h) which is not provided by TI-POSIX, it will get the one from the compiler. In some cases, the compiler does not provide the header file (e.g. #include <mqueue.h>) but it is provided by TI-POSIX. When a header file is provided by the compiler but needs additional content (e.g. errno.h), TI-POSIX will proxy the header file, include the same file from the compiler, and then add the necessary content.

For example, TI ARM compiler provides errno.h but does not define the ECONNREFUSED error code. When you compile your source code with TI-POSIX on the include path, this error code will be added.

#include <errno.h>

if (errno == ECONNREFUSED) {
    /* handle error */
}

When you compile this source file with TI-POSIX on the include path, the file include chain looks like this:

. /.../ti/posix/ccs/errno.h
.. /.../ti_cgt_arm/include/errno.h

After the compiler’s errno.h has been included, the TI-POSIX errno.h will reflect on the existing definitions, and if an error code is still missing, it will be added.

#ifndef ECONNREFUSED
#define ECONNREFUSED 111
#endif

This approach ensures all code remains compatible with existing error codes while only defining new error codes which are absent.

A similar approach is taken for other POSIX types and functions.

The TI-POSIX implementation must be compiled and linked into your program. The details depend on which RTOS kernel you are using.

Using TI-POSIX with TI-RTOS

To use TI-POSIX with the TI-RTOS kernel, simply add the following line to your kernel or application configuration script:

xdc.useModule('ti.posix.tirtos.Settings');

This will modify the kernel build such that the TI-POSIX implementation is compiled into the kernel library.

The Settings module is used to configure the behavior of the TI-POSIX implementation on TI-RTOS.

Settings.enableMutexPriority

This is a Boolean parameter that defaults to false. If set to true, the priority inheritance and priority ceiling mutex protocols will be available, which also increases code size. If these mutex protocols are not needed, leave the parameter at its default value.

Example configuration:

var Settings = xdc.useModule('ti.posix.tirtos.Settings');
Settings.enableMutexPriority = true;

Using TI-POSIX with FreeRTOS

We recommend you compile the TI-POSIX implementation along with the FreeRTOS kernel sources into the kernel library. To do this, simply add the following source files to your kernel build. Note that some compilers require additional source files.

ti/posix/freertos/clock.c
ti/posix/freertos/memory.c
ti/posix/freertos/mqueue.c
ti/posix/freertos/pthread_barrier.c
ti/posix/freertos/pthread_cond.c
ti/posix/freertos/pthread.c
ti/posix/freertos/pthread_mutex.c
ti/posix/freertos/pthread_rwlock.c
ti/posix/freertos/sched.c
ti/posix/freertos/semaphore.c
ti/posix/freertos/sleep.c
ti/posix/freertos/timer.c

Texas Instruments Compiler

ti/posix/freertos/PTLS.c
ti/posix/freertos/aeabi_portable.c

IAR Compiler

ti/posix/freertos/Mtx.c

Note that Texas Instruments does not provide a FreeRTOS port for all devices. When a FreeRTOS port is not available for a particular device, you must use TI-POSIX with TI-RTOS.

Binary Compatibility between TI-RTOS and FreeRTOS

When Texas Instruments provides both TI-RTOS and FreeRTOS for a given device, TI-POSIX supports binary compatibility between both kernels. This is useful when you are shipping libraries which use TI-POSIX. Such a library can be linked with either kernel.

To achieve binary compatibility without binding to a kernel, the POSIX object definitions have been constructed using opaque objects. This allows the compiler to allocate the proper space without binding the source code to kernel header files. However, this approach incurs a memory cost as the object size must be large enough to contain the kernel object with the largest footprint, regardless which kernel is in play. Also, the opaque object definitions are sensitive to changes in the kernel objects.

Therefore, be mindful of the kernel versions. Binary compatibility is only supported between the kernel versions which ship in a given SDK. Binary compatibility is not guaranteed when upgrading to a kernel version not shipped in the SDK. Please consult the SDK documentation to see which kernel versions are supported in that SDK release.

System Error Numbers

The compiler provides a definition for the errno symbol. In a single-thread application, errno may be a global identifier declared with external linkage. But in a multi-thread application, errno is typically stored in thread-local storage (TLS). This requires integration with the kernel. Each compiler provides a different implementation.

The TI-POSIX implementation is partitioned by compiler vendor.

CCS and TICLANG Compiler

By default, the compiler provides one global identifier for errno. However, when _AEABI_PORTABILITY_LEVEL is defined, errno becomes a function call to __aeabi_errno_addr(). The errno.h provided by TI-POSIX defines this symbol before including the compiler errno.h. In addition, TI-POSIX provides an implementation of __aeabi_errno_addr() which uses TLS. Finally, the TI-POSIX errno.h defines error codes which are omitted from the compiler header file.

/* file: /ti/posix/ccs/errno.h */

#define _AEABI_PORTABILITY_LEVEL 1

/* include compiler header file */
#include <../include/errno.h>

#ifndef EAGAIN
#define EAGAIN 11
#endif

/* ... */

Note: With newer versions of the TI ARM compiler, it is no longer necessary to define _AEABI_PORTABILITY_LEVEL in order to get the thread-safe implementation of errno. It is provided by default.

The CCS ARM compiler does not provide any TLS support. Therefore, the implementation of __aeabi_errno_addr() is dependent on which kernel is in play. However, the CCS C6x compiler does support TLS. With that compiler, the kernel integration makes use of the compiler TLS support functions.

Compiler Kernel Implementation Compiler Support RTS Stubs
TI-ARM TI-RTOS ti.sysbios.rts.ti.ReentSupport none __aeabi_errno_addr()
TI-ARM FreeRTOS ti.posix.freertos.PTLS none __aeabi_errno_addr()
TI-C6x TI-RTOS ti.sysbios.rts.ti.ThreadLocalStorage __TI_tls_block_size()
__TI_tls_init()
__c6xabi_get_tp()

GNU Compiler

The GCC compiler provides all necessary error numbers needed by the TI-POSIX implementation. We simply include the compiler’s header file.

/* file: /ti/posix/gcc/errno.h */

/* include compiler header file */
#include <../include/errno.h>

The GCC run-time support library, provides thread-safe access to the errno symbol through the use of a re-entrancy structure called struct _reent. The first element of this structure is used for storing a thread-specific errno value. The reent struct contains additional elements needed for making many of the Standard C library functions re-entrant.

The current reent structure is accessed either through the global pointer _impure_ptr, or by calling the function __getreent(). It is the responsibility of the kernel to manage the context of the current reent struct each time a thread switch occurs.

Texas Instruments provides a custom build of the Newlib-nano C run-time library built with re-entrancy support and which requires the kernel to implement the following functions.

Newlib-nano C library function stubs Comments
__getreent() Return the pointer to the current re-entrancy structure
__libc_lock_init() Initialize the system lock
__libc_lock_init_recursive() Initialize the recursive system lock
__libc_lock_acquire() Acquire the system lock
__libc_lock_acquire_recursive() Acquire the recursive system lock
__libc_lock_release() Release the system lock
__libc_lock_release_recursive() Release the recursive system lock
__libc_lock_try_acquire() Attempt to acquire the system lock
__libc_lock_try_acquire_recursive() Attempt to acquire the recursive system lock
__libc_lock_close() Finalize the system lock
__libc_lock_close_recursive() Finalize the recursive system lock

The implementation of these functions is provided by the following component:

Kernel Implementation
TI-RTOS ti.sysbios.rts.gnu.ReentSupport

When using FreeRTOS, the Newlib-nano C library provided by the GCC compiler is used. The FreeRTOS kernel manages the current reent struct by directly updating the global pointer _impure_ptr. Each task contains an embedded struct _reent structure in its TCB object. However, the system locks needed for thread-safety are not implemented.

The implementation of the reent struct in done directly in the FreeRTOS kernel when enabled with the NEWLIB_REENTRANT config macro.

Kernel Implementation
FreeRTOS FreeRTOS.h: configUSE_NEWLIB_REENTRANT = 1
task.c

IAR Compiler

The TI-POSIX errno.h header file for IAR defines error codes which are omitted from the compiler’s header file.

/* file: /ti/posix/iar/errno.h */

/* include compiler header file */
#include <../inc/c/errno.h>

#ifndef EAGAIN
#define EAGAIN 11
#endif

/* ... */

By default, the compiler provides one global identifier for errno. This symbol is implemented as a function call to __aeabi_errno_addr(), which returns the address of the global symbol. However, when linking with the IAR Multi-Thread RTS library (--threaded_lib), the implementation of this function makes use of Thread-Local Storage (TLS) for errno.

When using the Multi-Thread RTS library (MTS), there are several functions which have a binding to the kernel. For example, the allocation and management of the TLS block must be handled by the kernel. To facilitate this, the following functions in the MTS library must be implemented by the kernel.

MTS library function stubs Comments
__aeabi_read_tp() Return the pointer to the current TLS
__iar_system_Mtxinit() Initialize the system lock
__iar_system_Mtxdst() Finalize the system lock
__iar_system_Mtxlock() Acquire the system lock
__iar_system_Mtxunlock() Release the system lock

For a full descriptions of these functions, see the header file DLib_Threads.h in the compiler include folder.

The implementation of these functions is provided by the following components:

Kernel Implementation
TI-RTOS ti.sysbios.rts.iar.MultithreadSupport
FreeRTOS ti.posix.freertos.Mtx

Summary of functions supported in TI-POSIX

The following tables show the calling context for the POSIX functions supported in TI-POSIX. A supported function does not guarantee that all features of the API are available. Names with strike-through marks are not supported.

In the context of an RTOS, there are three execution contexts:

All supported POSIX functions may be invoked from a pthread.

A few POSIX functions may be invoked from main context or from task context (i.e. a native RTOS thread). These functions are typically used for creating a POSIX thread (i.e. pthread). The tables below indicate the valid calling context.

Although POSIX is a thread level API, a few functions have been implemented to support interrupt execution context. Keep in mind, that code written for interrupt context must always run to completion; do not make any blocking calls. These functions are listed below.

Thread Function Calling Context

Name Synopsis Main Task
pthread Thread Management
pthread_cancel Send a cancellation request to a thread No Yes
pthread_cleanup_pop Pop thread cancellation clean-up handler No Yes
pthread_cleanup_push Push thread cancellation clean-up handler No No
pthread_create Create a new thread Yes Yes
pthread_detach Detach a thread No Yes
pthread_equal Compare thread IDs Yes Yes
pthread_exit Terminate calling thread No No
pthread_getconcurrency Not supported
pthread_getcpuclockid Not supported
pthread_getschedparam Use to get priority of a pthread No Yes
pthread_getspecific Get thread-specific data for calling thread No No
pthread_join Join with a terminated thread No Yes
pthread_key_create Create a thread-specific data key No No
pthread_key_delete Delete a thread-specific data key No No
pthread_once Run an initialization routine once No Yes
pthread_self Obtain ID of the calling thread No No
pthread_setcancelstate Set cancel-ability state and type No No
pthread_setcanceltype Not supported. Only asynchronous cancellation is supported.
pthread_setconcurrency Not supported
pthread_setschedparam Use to set priority of a thread No No
pthread_setschedprio Not supported. Use pthread_setschedparam to set priority.
pthread_setspecific Set thread-specific data for calling thread No No
pthread_testcancel Not supported
pthread_attr Thread Attributes
pthread_attr_destroy Destroy thread attributes object Yes Yes
pthread_attr_getdetachstate Get detach state in attributes object Yes Yes
pthread_attr_getguardsize Get guard size in attributes object Yes Yes
pthread_attr_getinheritsched Not supported - kernel scheduling policy is fixed
pthread_attr_getschedparam Get scheduling parameters in attributes object Yes Yes
pthread_attr_getschedpolicy Not supported - kernel scheduling policy is fixed
pthread_attr_getscope Not supported - kernel has no notion of process
pthread_attr_getstack Get stack size and address attributes Yes Yes
pthread_attr_getstacksize Get stack size in attributes object Yes Yes
pthread_attr_init Initialize thread attributes object Yes Yes
pthread_attr_setdetachstate Set detach state in attributes object Yes Yes
pthread_attr_setguardsize Set guard size in attributes object Yes Yes
pthread_attr_setinheritsched Not supported - kernel scheduling policy is fixed
pthread_attr_setschedparam Set scheduling parameters in attributes object Yes Yes
pthread_attr_setschedpolicy Not supported - kernel scheduling policy is fixed
pthread_attr_setscope Not supported - kernel has no notion of process
pthread_attr_setstack Set the stack size and address Yes Yes
pthread_attr_setstacksize Set the stack size Yes Yes
pthread_barrier Thread Synchronization
pthread_barrier_destroy Destroy a barrier object Yes Yes
pthread_barrier_init Initialize a barrier object Yes Yes
pthread_barrier_wait Synchronize at a barrier No Yes
pthread_barrierattr Barrier Attributes
pthread_barrierattr_destroy Destroy the barrier attributes object Yes Yes
pthread_barrierattr_getpshared Not supported - kernel has no notion of process
pthread_barrierattr_init Initialize the barrier attributes object Yes Yes
pthread_barrierattr_setpshared Not supported - kernel has no notion of process
pthread_cond Condition Variable
pthread_cond_broadcast Unblock all threads blocked on a condition variable No Yes
pthread_cond_destroy Free resources allocated for a condition variable Yes Yes
pthread_cond_init Allocate and initialize a condition variable Yes Yes
pthread_cond_signal Unblock a thread waiting on a condition variable No Yes
pthread_cond_timedwait Wait on a condition variable with a timeout No Yes
pthread_cond_wait Wait on a condition variable No Yes
pthread_condattr Condition Variable Attributes
pthread_condattr_destroy Destroy condition variable attributes object Yes Yes
pthread_condattr_getclock Not supported
pthread_condattr_getpshared Not supported
pthread_condattr_init Initialize condition variable attributes object Yes Yes
pthread_condattr_setclock Not supported
pthread_condattr_setpshared Not supported
pthread_mutex Mutual Exclusion
pthread_mutex_destroy Free resources allocated for a mutex Yes Yes
pthread_mutex_getprioceiling Get the priority ceiling of a mutex [1][3] Yes Yes
pthread_mutex_init Allocate and initialize a mutex Yes Yes
pthread_mutex_lock Lock a mutex No No
pthread_mutex_setprioceiling Set the priority ceiling of a mutex [1][2][3] No Yes
pthread_mutex_timedlock Wait for a mutex with a timeout No No
pthread_mutex_trylock Lock a mutex if it is available, return without blocking No No
pthread_mutex_unlock Unlock a mutex owned by the calling thread No No
pthread_mutexattr Mutex Attributes
pthread_mutexattr_destroy Destroy a mutex attributes object Yes Yes
pthread_mutexattr_getprioceiling Get the priority ceiling of the mutex attributes object [3] Yes Yes
pthread_mutexattr_getprotocol Get to protocol of the mutex attributes object [3] Yes Yes
pthread_mutexattr_getpshared Not supported
pthread_mutexattr_gettype Get the mutex type attribute Yes Yes
pthread_mutexattr_init Initialize a mutex attributes object Yes Yes
pthread_mutexattr_setprioceiling Set the priority ceiling of the mutex attributes object [3] Yes Yes
pthread_mutexattr_setprotocol Set the protocol of the mutex attributes object [3] Yes Yes
pthread_mutexattr_setpshared Not supported
pthread_mutexattr_settype Set the mutex type attribute Yes Yes
pthread_rwlock Read-Write Lock
pthread_rwlock_destroy Destroy a read-write lock object Yes Yes
pthread_rwlock_init Initialize a read-write lock object Yes Yes
pthread_rwlock_rdlock Lock a read-write lock object for reading No No
pthread_rwlock_timedrdlock Lock a read-write lock object for reading with a timeout No No
pthread_rwlock_timedwrlock Lock a read-write lock for writing with a timeout No No
pthread_rwlock_tryrdlock Attempt a read-write lock for reading (non-blocking) No No
pthread_rwlock_trywrlock Attempt a read-write lock for writing (non-blocking) No No
pthread_rwlock_unlock Unlock a read-write lock object No No
pthread_rwlock_wrlock Lock a read-write lock object for writing No No
pthread_rwlockattr Read-Write Lock Attributes
pthread_rwlockattr_destroy Destroy the read-write lock attributes object Yes Yes
pthread_rwlockattr_getpshared Not supported
pthread_rwlockattr_init Initialize the read-write lock attributes object Yes Yes
pthread_rwlockattr_setpshared Not supported

Miscellaneous Function Calling Context

Name Synopsis Main Task
clock
clock_gettime Get the current time Yes Yes
clock_nanosleep High resolution sleep with specifiable clock No Yes
clock_settime Set the current time for the CLOCK_REALTIME clock Yes Yes
message queue
mq_close Close a message queue Yes Yes
mq_getattr Get message queue attributes Yes Yes
mq_open Open a message queue Yes Yes
mq_receive Receive a message from a message queue No Yes
mq_send Send a message to a message queue No Yes
mq_setattr Set message queue attributes Yes Yes
mq_timedreceive Receive a message from a message queue, with timeout No Yes
mq_timedsend Send a message to a message queue, with timeout No Yes
mq_unlink Remove a message queue Yes Yes
semaphore
sem_destroy Destroy a semaphore Yes Yes
sem_getvalue Get the semaphore count Yes Yes
sem_init Initialize a semaphore Yes Yes
sem_post Unlock a semaphore (i.e. increment the semaphore count) Yes Yes
sem_timedwait Lock a semaphore, with timeout No Yes
sem_trywait Attempt to lock semaphore (non-blocking) No Yes
sem_wait Lock a semaphore No Yes
sleep
nanosleep High resolution sleep No Yes
timer
timer_create Create a timer Yes Yes
timer_delete Delete a timer Yes Yes
timer_gettime Fetch the amount of time until the timer expires Yes Yes
timer_settime Set the time until the next expiration of the timer Yes Yes

Interrupt Calling Context

The following functions may be invoked from interrupt context. Note the non-blocking requirement.

Name Synopsis
sem_post Unlock a semaphore (i.e. increment the semaphore count)
mq_getattr Get message queue attributes
mq_receive (non-blocking only) Receive a message from a message queue
mq_send (non-blocking only) Send a message to a message queue
mq_timedreceive (non-blocking only) Receive a message from a message queue
mq_timedsend (non-blocking only) Send a message to a message queue

Implementation Notes

The following sections provide some notes on the TI-POSIX implementation details.

Thread default stack size and thread default priority

int pthread_attr_init(pthread_attr_t *attr)

pthread_attr_init() initializes the pthread_attr_t object pointed to by attr with default values, which includes the stack size and thread priority.

On TI-RTOS, the default stack size is defined in the configuration script.

var Task = xdc.useModule('ti.sysbios.knl.Task');
Task.defaultStackSize = 0x800;

On FreeRTOS, the default stack size is computed as follows using values defined in FreeRTOSConfig.h and portmacro.h header files.

configPOSIX_STACK_SIZE * sizeof(portSTACK_TYPE)

Note that configPOSIX_STACK_SIZE is a custom symbol defined the FreeRTOSConfig.h header file which is shipped in the SDK. The portmacro.h header file is shipped in the FreeRTOS distribution.

The priority is set to 1, which is the lowest priority allowed for a task other than the Idle task, which is priority 0.

Thread scheduling policy

int pthread_getschedparam(pthread_t pthread, int *policy, struct sched_param *param)
int pthread_setschedparam(pthread_t pthread, int policy, const struct sched_param *param)

The RTOS kernel uses a priority-based scheduler. The policy parameter is ignored in these functions.

TI-POSIX on FreeRTOS

Best effort has been made to provide the same support between TI-RTOS and FreeRTOS. However, there are some exceptions, which are noted here.

There is no way to pass a stack to xTaskCreate()

pthread_attr_setstack() - not supported on FreeRTOS

Priority inheritance for mutexes is handled by FreeRTOS. The priority of a task that owns a mutex is temporarily raised, if a task of higher priority attempts to acquire the mutex. Therefore, mutex protocols are not supported on FreeRTOS.

pthread_mutexattr_getprotocol() - not supported for FreeRTOS
pthread_mutexattr_setprotocol() - not supported for FreeRTOS
pthread_mutexattr_getprioceiling() - not supported for FreeRTOS
pthread_mutexattr_setprioceiling() - not supported for FreeRTOS

Only CLOCK_REALTIME is supported with clock_settime().

timer_settime() - this is a blocking call, unlike on TI-RTOS

Task cleanup is done by the Idle task in FreeRTOS. If pthreads are deleted, make sure the Idle task gets a chance to run to free allocated memory for the deleted thread.

Binary Semaphore

POSIX does not support a binary semaphore. That is, a semaphore which can be posted multiple times, but the count goes no greater than 1. The next pend will reduce the count to 0.

Fortunately, it is easy to build a binary semaphore using a mutex and condition variable. The following is a pseudo code example.

struct binary_semaphore {
    pthread_mutex_t mutex;
    pthread_cond_t cvar;
    int v;
};

void mysem_post(struct binary_semaphore *p)
{
    pthread_mutex_lock(&p->mutex);
    p->v = 1;
    pthread_cond_signal(&p->cvar);
    pthread_mutex_unlock(&p->mutex);
}

void mysem_pend(struct binar_semaphore *p)
{
    pthread_mutex_lock(&p->mutex);
    while (!p->v) {
        pthread_cond_wait(&p->cvar, &p->mutex);
    }
    p->v = 0;
    pthread_mutex_unlock(&p->mutex);
}