Developing multithreaded programs

Developing multithreaded programs is similar to developing programs with multiple processes. Developing programs also consists of compiling and debugging the code.

Compiling a multithreaded program

This section explains how to generate a multithreaded program. It describes the following:
  • The required header file
  • Invoking the compiler, which is used to generate multithreaded programs.

Header file

All subroutine prototypes, macros, and other definitions for using the threads library are in the pthread.h header file, which is located in the /usr/include directory. The pthread.h header file must be included in each source file using the threads library.

The pthread.h header includes the unistd.h header, which provides the following global definitions:

_POSIX_REENTRANT_FUNCTIONS
Specifies that all functions should be reentrant. Several header files use this symbol to define supplementary reentrant subroutines, such as the localtime_r subroutine.
_POSIX_THREADS
Denotes the POSIX threads API. This symbol is used to check if the POSIX threads API is available. Macros or subroutines may be defined in different ways, depending on whether the POSIX or some other threads API is used.

The pthread.h file also includes errno.h, in which the errno global variable is redefined to be thread-specific. The errno identifier is, therefore, no longer an l-value in a multithreaded program.

Invoking the compiler

When compiling a multithreaded program, invoke the C compiler using one of the following commands:
xlc_r
Invokes the compiler with default language level of ansi
cc_r
Invokes the compiler with default language level of extended

These commands ensure that the adequate options and libraries are used to be compliant with the Single UNIX Specification, Version 2. The POSIX Threads Specification 1003.1c is a subset of the Single UNIX Specification, Version 2.

The following libraries are automatically linked with your program when using the xlc_r and cc_r commands:
libpthreads.a
Threads library
libc.a
Standard C library
For example, the following command compiles the foo.c multithreaded C source file and produces the foo executable file:
cc_r -o foo foo.c

Invoking the compiler for draft 7 of POSIX 1003.1c

AIX® provides source code compatibility for Draft 7 applications. It is recommended that developers port their threaded application to the latest standard.

When compiling a multithreaded program for Draft 7 support of threads, invoke the C compiler using one of the following commands:
xlc_r7
Invokes the compiler with default language level of ansi
cc_r7
Invokes the compiler with default language level of extended
The following libraries are automatically linked with your program when using the xlc_r7 and cc_r7 commands:
libpthreads_compat.a
Draft 7 Compatibility Threads library
libpthreads.a
Threads library
libc.a
Standard C library

To achieve source code compatibility, use the compiler directive _AIX_PTHREADS_D7. It is also necessary to link the libraries in the following order: libpthreads_compat.a, libpthreads.a, and libc.a. Most users do not need to know this information, because the commands provide the necessary options. These options are provided for those who do not have the latest AIX compiler.

Porting draft 7 applications to the &Symbol.unixspec;

Differences exist between Draft 7 and the final standard include:
  • Minor errno differences. The most prevalent is the use of ESRCH to denote the specified pthread could not be found. Draft 7 frequently returned EINVAL for this failure.
  • The default state when a pthread is created is joinable. This is a significant change because it can result in a memory leak if ignored.
  • The default pthread scheduling parameter is scope.
  • The pthread_yield subroutine has been replaced by the sched_yield subroutine.
  • The various scheduling policies associated with the mutex locks are slightly different.

Memory requirements of a multithreaded program

AIX supports up to 32768 threads in a single process. Each individual pthread requires some amount of process address space, so the actual maximum number of pthreads that a process can have depends on the memory model and the use of process address space, for other purposes. The amount of memory that a pthread needs includes the stack size and the guard region size, plus some amount for internal use. The user can control the size of the stack with the pthread_attr_setstacksize subroutine and the size of the guard region with the pthread_attr_setguardsize subroutine.
Note: The soft limit on stack size imposed by the command ulimit –s applies only to the stack of the main thread of the application.
The following table indicates the maximum number of pthreads that could be created in a 32-bit process using a simple program which does nothing other than create pthreads in a loop using the NULL pthread attribute. In a real program, the actual numbers depend on other memory usage in the program. For a 64-bit process, the ulimit subroutine controls how many threads can be created. Therefore, the big data model is not necessary and in fact, can decrease the maximum number of threads.
Data Model -bmaxdata Maximum Pthreads
Small Data n/a 1084
Big Data 0x10000000 2169
Big Data 0x20000000 4340
Big Data 0x30000000 6510
Big Data 0x40000000 8681
Big Data 0x50000000 10852
Big Data 0x60000000 13022
Big Data 0x70000000 15193
Big Data 0x80000000 17364
The NUM_SPAREVP environment variable can be set to control the number of spare virtual processors that are maintained by the library. It is not necessary to modify this variable. In some circumstances, applications that use only a few megabytes of memory can reduce memory overhead by setting the NUM_SPAREVP environment variable to a lower value. Typical settings include the number of CPUs on the system or the peak number of process threads. Setting this variable does not affect process performance. The default setting is 256.
Note: The NUM_SPAREVP environment variable is available only in AIX 5.1.

Example of a multithreaded program

The following short multithreaded program displays "Hello!" in both English and French for five seconds. Compile with cc_r or xlc_r. F
#include <pthread.h>    /* include file for pthreads - the 1st */
#include <stdio.h>      /* include file for printf()           */
#include <unistd.h>     /* include file for sleep()            */

void *Thread(void *string)
{
        while (1)
                printf("%s\n", (char *)string);
        pthread_exit(NULL);
}

int main()
{
        char *e_str = "Hello!";
        char *f_str = "Bonjour !";

        pthread_t e_th;
        pthread_t f_th;

        int rc;

        rc = pthread_create(&e_th, NULL, Thread, (void *)e_str);
        if (rc)
                exit(-1);
        rc = pthread_create(&f_th, NULL, Thread, (void *)f_str);
        if (rc)
                exit(-1);
        sleep(5);

        /* usually the exit subroutine should not be used
           see below to get more information */
        exit(0);
}

The initial thread (executing the main routine) creates two threads. Both threads have the same entry-point routine (the Thread routine), but a different parameter. The parameter is a pointer to the string that will be displayed.

Debugging a multithreaded program

The following tools are available to debug multithreaded programs:
  • Application programmers can use the dbx command to perform debugging. Several subcommands are available for displaying thread-related objects, including attribute, condition, mutex, and thread.
  • Kernel programmers can use the kernel debug program to perform debugging on kernel extensions and device drivers. The kernel debug program provides limited access to user threads, and primarily handles kernel threads. Several subcommands support multiple kernel threads and processors, including:
    • The cpu subcommand, which changes the current processor
    • The ppd subcommand, which displays per-processor data structures
    • The thread subcommand, which displays thread table entries
    • The uthread subcommand, which displays the uthread structure of a thread
    For more information on the kernel debug program, see the Kernel Extensions and Device Support Programming Concepts.

Core File requirements of a multithreaded program

By default, processes do not generate a full core file. If an application must debug data in shared memory regions, particularly thread stacks, it is necessary to generate a full core dump. To generate full core file information, run the following command as root user:
	chdev -l sys0 -a fullcore=true

Each individual pthread adds to the size of the generated core file. The amount of core file space that a pthread needs includes the stack size, which the user can control with the pthread_attr_setstacksize subroutine. For pthreads created with the NULL pthread attribute, each pthread in a 32-bit process adds 128 KB to the size of the core file, and each pthread in a 64-bit process adds 256 KB to the size of the core file.