Porting DCE Threads Programs to HP-UX 11.0 POSIX Threads

By

Tom Shem

Application Delivery Lab

Hewlett-Packard

----------------------------------------------------------------------------

Introduction

HP-UX Version 11.0 provides a POSIX threads package based on the POSIX.1-1996 standard. This article addresses the needs of the users who want to migrate existing DCE pthreads-based applications to POSIX Threads. The article is written as a set of tables containing the differences between the DCE pthreads and the HP-UX pthreads. Since the reentrant C library is part of the POSIX.4a specification, differences in these are also presented. Significant changes between the two POSIX threads specifications that pertain to porting DCE-based applications to HP-UX POSIX Threads are described in detail. This paper is written for an audience who is familiar with DCE threads. It is not meant to be a tutorial for how to write threaded applications. ----------------------------------------------------------------------------

DCE Pthreads Content

The HP DCE package (version 1.7) shipped in HP-UX provides support for a threads package derived from The Open Group's DCE 1.2.1 version which is based on the POSIX.4a draft 4 API and several extensions. In addition, it provides an exception support not specified in the POSIX.4a. It consists of the following components: * Base Functionality o POSIX.4a draft 4 API o Draft 4 Signal Subsystem o Support for POSIX.1 API o Reentrant C Library * Extensions o Non-portable extensions to the pthread API (with suffix: _np) o Exception returning API (instead of error returns) o Exception Handling (TRY/CATCH) The HP-UX POSIX threads support (the pthreads and reentrant C library functions based on the POSIX.1-1996 std) introduced changes to the draft 4 as follows: * Syntax changes to some of the API in draft 4. * All pthread functions now return error codes and not set the global errno. * New functions introduced in the POSIX standard to enhance thread-specific data handling, scheduling. * Differences in signal handling between draft 4 and the POSIX standard. * Specification of cancellation points. * Changes to the Reentrant Library API in the POSIX standard. ----------------------------------------------------------------------------

Pthreads Programming Model

The DCE Pthreads provides a set of library functions to facilitate parallel programming as well as realtime scheduling. In this model, the programmer identifies sequences of program flow that can be parallelized. After this is done, these parallel sequences of execution can be hoisted on the so-called threads. The pthreads programming model preserves the POSIX.1 functionality. In particular, all I/O work on a per-thread basis. For instance, a thread issuing a blocking read() blocks itself; and not all other threads. The pthreads programming model is written as an extension to the POSIX.4 which is a standard for Real Time Extension to the POSIX.1. As such, the POSIX.4a preserves the POSIX.4 semantics on a per-thread basis. The pthread model adds the following features to enable parallel and realtime programming: * Thread Management Functions * Synchronization Functions * Scheduling Functions to meet realtime requirements * Thread-Specific Data Functions * Thread Cancellation Functions The DCE pthreads package (also known as CMA threads, or user-space threads; or DCE pthreads) is a purely user-level threads package; there is no kernel support in HP-UX CMA threads. HP-UX 11.0 provides kernel support with POSIX threads and hence, the POSIX threads are supported over these kernel threads. HP-UX 11.0 POSIX threads provides a 1:1 mapping of user-level threads to kernel threads. This means that for every user-level thread, there is a corresponding kernel thread. At this time, HP-UX allows a default of 64 simultaneous threads in a user process. This limit is configurable. ----------------------------------------------------------------------------

DCE Pthreads Package

On HP-UX 9.x, DCE pthreads provides the Reentrant C Library (libc_r.a), the pthreads API based on POSIX.4a draft 4, the signal handling and the exception handling support. Applications written to DCE Pthreads link with libc_r.a and libpthreads.a. Applications that are multithreaded should use cc_r for compiling threaded programs written to DCE pthreads. On HP-UX 10.x and 11.0, improvements were made to libc so that the Reentrant C Library (libc_r.a) and cc_r are no longer necessary (they are still provided for backward compatibility reasons). Compilation and linking can be accomplished by using the standard cc or c89 and libc. ----------------------------------------------------------------------------

Porting Issues

This section details the changes made from DCE 1.2.1 pthreads to the HP-UX 11.0 pthreads library. Significant differences are explained in detail.

Changes in Error Returns

All HP-UX 11.0 pthreads functions return error number as the return value (they also set the errno global variable). In DCE pthreads, the pthreads functions on error, return a -1 and set errno global variable to the appropriate value. Applications written to DCE pthreads need to be changed to handle error returns properly. For example: DCE Pthreads: {int ret; int *per; ret = pthread_cond_t ( thread, &per ) ; if ( ret== -1 ) printf ( error =%d\n , errno ) ; } HP-UX POSIX pthreads {int ret; int *ptr; ret = pthread_join ( thread, &ptr ) ; if ( ret ) printf ( error =%d\n , ret ) ; }

Data Types

The DCE 1.2.1 pthreads introduced the following data types that are not part of the POSIX.4a drafts. These should be replaced with those that are specified in the POSIX.1-1996 standard. These are: pthread_startroutine_t, pthread_addr_t, pthread_destructor_t and pthread_initroutine_t. These need to be replaced as shown below: Data Types Not Part of POSIX.4a DCE 1.2.1 pthreads HP-UX POSIX pthreads ____________________ ________________________ pthread_startroutine_t start_routine void * ( *start_routine ) ( void * ) pthread_addr_t arg void *arg pthread_destructor_t destructor void ( *destructor ( void * ) ) pthread_initroutine_t init_routine void ( *init_routine ) ( void )

Exception Handling

DCE 1.2.1 supports exceptions to handle error conditions and thread cancellations. It raises exceptions under the following situations: * An exception is raised when the current thread is canceled. If a TRY/CATCH block exists around the cancel point (such as a pthread_join() call) , the appropriate CATCH block is entered. If there is no TRY/CATCH block, the thread that is canceled terminates execution. A point to note there is that the POSIX.1-1996 standard specifies the method of cleanup upon cancellation through the two functions: pthread_cleanup_push() and pthread_cleanup_pop(). The DCE 1.2.1 pthreads integrated these two mechanisms such that if a TRY/CATCH as well as a cleanup handler exists, both are invoked in the last-in-first-out (LIFO) order. * When the signals SIGPIPE or SIGSYS are received in the current thread, a corresponding exception is raised (unless the signal is set to be ignored). If a TRY/CATCH block exists for handling these exceptions, then, the proper block is entered. * A parallel set of pthread API is supported that raise exceptions instead of return errors. These API are invoked when a program includes pthread_exc.h instead of pthread.h header file. In pthread_exc.h the pthread_*() functions are mapped into corresponding exception raising API. HP-UX POSIX pthreads does not support exception handling as it is not specified in the POSIX.1-1996 Standard. Hence programs written to handle exceptions need to be re-written to work on HP-UX. For cancellation cleanup handling, replace TRY/CATCH with pthread_cleanup_push() and pthread_cleanup_pop() way of handling cleanup upon thread cancellations. Replace all other exception handling with code that handles error returns from the pthread_*() calls.

POSIX.1 Support

All POSIX.1 functions and C standard that suspend the calling process are redefined to suspend the calling thread in POSIX.4a. The DCE pthreads package in HP-UX implements this except in the case of the following functions, which suspend the entire process: waitpid(), wait(), sigsuspend(), pause() and tcdrain(). Also, DCE pthreads package does not support timed input operations using the VMIN and VTIME (through the ioctl()). These are supported in HP-UX 11.0.

Non-Blocking I/O Support

The DCE pthreads provides support for POSIX.1 I/O functions through a library emulation of nonblocking I/O. However, not all functions are supported. For instance, there is no non-blocking support for: shared memory, System V semaphores, message queue operations and poll(). This is a non-issue in HP-UX 11.0 with POSIX threads as all the I/O calls are supported to block only the calling thread and not the entire process. As such, all the POSIX.1 I/O functions are supported with POSIX threads in HP-UX 11.0.

Thread Functions

Changes in Thread Detach

In DCE 1.2.1 pthreads, you can specify the detach state by calling the function: pthread_detach() with the thread ID as the argument. In HP-UX 11.0, you can specify the thread detach state either at the time of thread creation (by specifying the thread attribute) or, after the creation through the use of pthread_detach(). The default thread detach state is PTHREAD_CREATE_JOINABLE.

Changes in Scheduling

Significant changes in thread scheduling due to integration of scheduling with the Realtime Standard (POSIX.4). Specifically, the sched_param structure is used in HP-UX to specify the scheduling policy and priority. The new structure is as follows: struct sched_param { int sched_policy; int sched_priority; } You can use the thread attribute for scheduling to specify the scheduling policy and priority. * Set the sched_policy and sched_priority in the thread attribute object by specifying a sched_param structure with the desired values for policy and priority and invoke: * Applications that used: pthread_attr_setprio() or pthread_attr_getprio() can be rewritten to use the sched_param structure to specify the priority. * The DCE pthreads priority symbols (PRI_FIFO_*, PRI_RR_*, etc) were removed from the POSIX.1-1996 standard and are not supported with POSIX threads on HP-UX 11.0. Implementation must call the sched_get_priority_max() and sched_get_priority_min() to obtain scheduling priority limits.

Differences in Mutex

Changes Introduced in HP-UX POSIX pthreads: * Static Initialization of Mutexes * POSIX.1-1996 defines 4 types of mutexes: PTHREAD_MUTEX_NORMAL, PTHREAD_MUTEX_RECURSIVE, PTHREAD_MUTEX_ERRORCHECK, PTHREAD_MUTEX_DEFAULT * Scheduling attributes to prevent unbounded priority inversion specified in the POSIX.1-1996 standard are available (i.e. dynamic priority of mutexes and dynamic ceiling of mutexes). However, mutex priority inversion is currently not supported on HP-UX 11.0. * Note the change from pthread_mutexattr_create() to pthread_mutexattr_init(). * Note the change from pthread_mutexattr_delete() to prhread_mutexattr_destroy(). Differences in Condition Variables * Static initialization of condition variables is permitted in HP-UX. * Note the change from: pthread_condattr_create() to pthread_condattr_init(). * Note the change from: pthread_condattr_delete() to pthread_condattr_destroy().

Thread Cancellation

New Functions Introduced in HP-UX pthreads (based on POSIX.1-1996): * Separation of the concepts of state of cancelability from the type of cancelability. The possible states are: enable or disable. The possible types are: deferred or asynchronous. Porting Issues * The DCE 1.2.1 pthread functions (draft 4 based) : pthread_setcancel(int state) sets general cancelability. If a thread's general cancelability is ON, then, its cancelability is controlled by the state of the async cancelability. If its async cancelability is ON, it can be canceled at any point. If not, the thread can be canceled at specific cancellation points (such as condition waits, thread joins and calls to pthread_testcancel()). The default state of cancelability in DCE is: general cancelability is ON and Async cancelability is OFF. This is equivalent to: pthread_setcancelstate ( PTHREAD_CANCEL_ENABLE, &oldstate ) ; pthread_setcanceltype ( PTHREAD_CANCEL_DEFERRED, &oldtype ) ; * The DCE 1.2.1-based applications should be re-written as follows: Thread Cancellation DCE 1.2.1 pthreads HP-UX pthreads ____________________ ________________________ pthread_setcancel ( CANCEL_ON ) pthread_setcancelstate ( PTHREAD_CANCEL_ENABLE ) pthread_setcancel ( CANCEL_OFF ) pthread_setcancelstate ( PTHREAD_CANCEL_DISABLE ) pthread_setasynccancel ( CANCEL_ON ) pthread_setcanceltype ( PTHREAD_CANCEL_ASYNCHRONOUS ) pthread_setasynccancel ( CANCEL_OFF ) pthread_setcanceltype ( PTHREAD_CANCEL_DEFERRED )

Process Primitives

Process Primitives DCE 1.2.1 pthreads HP-UX pthreads ____________________ ________________________ void atfork ( void *user_state, int pthread_atfork ( void void ( *pre_fork )() , ( *prepare ) ( void ) , void ( *parent_fork )() , void (*parent ) ( void ) , void ( *child_fork )() ) void ( *child ) ( void )

Signal Subsystem Differences

New Functionality introduced in HP-UX (based on POSIX.1-1996) * Removal of the per-thread signal handler. In HP-UX, all signal handlers are installed on a per-process basis. * Per-thread signal masks (in draft 4, the signal masks were per-process). * Changes to sigwait() syntax to allow for signal received as an output argument. * SIGEV_THREAD as defined in POSIX.1-1996 is currently not supported on HP-UX 11.0. Porting Issues Signal Handlers * DCE 1.2.1 o DCE 1.2.1 Pthreads allows two types of signal handlers: per-thread signal handler for synchronous signals (SIGILL, SIGTRAP, SIGIOT, SIGEMT, SIGFPE, SIGBUS, SIGSEGV, SIGSYS and SIGPIPE) and per-process signal handlers for all other signals except for SIGKILL, SIGSTOP and SIGVIRT. These handers can be installed using the sigaction() or signal() API. o In DCE 1.2.1 pthreads, sigwait() can be done on all asynchronous signals; this list excludes the synchronous signals listed above and the signals SIGKILL, SIGSTOP and SIGVIRT. The signal SIGVIRT is used to the thread time slicing (scheduling) and hence is not available to any application program. In HP-UX, SIGVIRT (37) , SIGALRM1 (38) and SIGWAITING (39) are reserved for internal use and hence handlers cannot be installed for these signals. * HP-UX POSIX pthreads o In HP-UX, all signal handlers are installed on a per-process basis. Hence, programs installing signal handlers need to be aware that they may replace existing handlers installed by other threads. o In HP-UX, you can install signal handlers for all signals except: SIGKILL (9), SIGSTOP (24). o In HP-UX pthreads, sigwait() can be done all asyncronous signals; this list excludes the synchronous signals listed above and the signals: SIGKILL (9) and SIGSTOP (24). Per-Process Signal Mask * DCE 1.2.1 Pthreads allows use of sigprocmask() to block signals for the entire process (not just the calling thread) whereas in HP-UX, the sigthreadmask() is used to specify the block mask on a per-thread basis. Use of sigprocmask() is unspecified in a multithreaded environment (POSIX.4a draft 7) ; in HP-UX, sigprocmask() is mapped to sigthreadmask(). ----------------------------------------------------------------------------

Details

CMA thread function mapping to POSIX thread function mapping

* See Table 1.

Reentrant Functions (e.g. libc_r (or equivalent))

Changes Introduced in HP-UX ( POSIX.1-1996 Std ) * The POSIX.1-1996 Standard removed requirements for libc_r. Most library functions could be made thread-safe without modifying the function interface. However, a few functions could not. In these cases, new functions containing the _r suffix were added. * Functions that return pointers into library allocated buffers of indeterminate size are required to allocate these buffers on a per-thread basis. * Functions Requiring Explicit Client Locking Changes introduced in HP-UX ( POSIX.1-1996 Std ) DCE 1.2.1 libc_r HP-UX Pthreads "_r" routines __________________ ____________________________ int localtime_r ( struct tm *result, struct tm* localtime_r ( const time_t *clock ) time_t *clock, struct tm *result ) int gmtime_r ( struct tm *result, struct tm *gmtime_r ( const time_t time_t *clock ) *clock, struct tm *result ) int asctime_r ( struct tm *tm, char char *asctime_r ( const struct *buffer, int buflen ) tm *tm, char *buf ) char *ctime_r ( time_t *clock, char char *ctime_r ( const time_t *clock, char *buf ) *clock, char *buf ) int getpwnam_r ( struct passwd *result, int getpwnam_r (const char *nam, char *nam, char *buffer, int buflen ) struct passwd *pwent, char *line, int len ) int getpwuid_r ( struct passwd *result, int getpwuid_r ( uid_t uid, int uid, char *buffer, int buflen ) struct passwd *pwent, char *line, int len ) int getgrgid_r ( struct group *result, int getgrgid_r ( gid_t gid, struct int gid, char *buffer, int buflen ) group *grent, char *line, int len ) int getgrnam_r ( struct group *result, int getgrnam_r ( const char char *name, char *buffer, int buflen ) *nam, struct group *grent, char *line, int len ) int readdir_r ( DIR *dirp, struct int readdir_r ( DIR *dirp, dirent *result ) struct dirent *entry, struct dirent **result ) int getlogin_r(char *name, size_t namesize); int rand_r(unsigned int *seed); char *strtok_r(char *s, const char *sep, char **lasts); int ttyname_r(int fildes, char *name, size_t namesize); ----------------------------------------------------------------------------

Table 1:

This table provides a mapping between CMA threads functions and POSIX threads functions with equivalent functionality. Consult the HP-UX man pages for function prototype details. CMA Name Available in POSIX Name ---------------------------------------------------------------------------- atfork libcma only pthread_atfork hp__pthread_equal libcma only pthread__cancel_thread libcma only pthread_cancel pthread_attr_create libcma only pthread_attr_init pthread_attr_delete libcma only pthread_attr_destroy pthread_attr_getguardsize_np libcma only pthread_attr_getguardsize pthread_attr_getinheritsched libcma only pthread_attr_getinheritsched pthread_attr_getprio libcma only pthread_attr_getschedparam pthread_attr_getsched libcma only pthread_attr_getschedpolicy pthread_attr_getstacksize Both. pthread_attr_getstacksize pthread_attr_setguardsize_np libcma only pthread_attr_setguardsize pthread_attr_setinheritsched Both. pthread_attr_setinheritsched pthread_attr_setprio libcma only pthread_attr_setschedparam pthread_attr_setsched libcma only pthread_attr_setschedpolicy pthread_attr_setstacksize Both pthread_attr_setstacksize pthread_cancel Both. pthread_cancel pthread_cond_broadcast Both. pthread_cond_broadcast pthread_cond_destroy Both. pthread_cond_destroy pthread_cond_init Both. pthread_cond_init pthread_cond_signal Both. pthread_cond_signal pthread_cond_signal_int_np libcma only pthread_cond_timedwait Both. pthread_cond_timedwait pthread_cond_wait Both. pthread_cond_wait pthread_condattr_create libcma only pthread_condattr_init pthread_condattr_delete libcma only pthread_condattr_destroy pthread_create Both. pthread_create pthread_ctxcb_hpux libcma only pthread_delay_np libcma only nanosleep() pthread_detach Both. pthread_detach pthread_equal Both. pthread_equal pthread_exit Both. pthread_exit pthread_get_expiration_np libcma only get_expiration_time() pthread_getprio libcma only pthread_getschedparam pthread_getscheduler libcma only pthread_getschedparam pthread_getspecific Both. pthread_getspecific pthread_is_multithreaded_np libcma only pthread_join Both. pthread_join pthread_keycreate libcma only pthread_key_create pthread_lock_global_np libcma only pthread_mutex_destroy Both. pthread_mutex_destroy pthread_mutex_init Both. pthread_mutex_init pthread_mutex_lock Both. pthread_mutex_lock pthread_mutex_trylock Both. pthread_mutex_trylock pthread_mutex_unlock Both. pthread_unlock pthread_mutexattr_create libcma only pthread_mutexattr_init pthread_mutexattr_delete libcma only pthread_mutexattr_destroy pthread_mutexattr_getkind_np libcma only pthread_mutexattr_setkind_np libcma only pthread_once Both. pthread_once pthread_self Both. pthread_self pthread_setasynccancel libcma only pthread_setcanceltype pthread_setcancel libcma only pthread_setcancelstate pthread_setprio libcma only pthread_setschedparam pthread_setscheduler libcma only pthread_setschedparam pthread_setspecific Both. pthread_setspecific pthread_signal_to_cancel_np libcma only pthread_testcancel Both. pthread_testcancel pthread_unlock_global_np libcma only pthread_yield libcma only sigprocmask libcma only pthread_sigmask sigwait Both sigwait

Data Types

pthread_attr_default libcma only Parameter value NULL. pthread_condattr_default libcma only Parameter value NULL. pthread_mutexattr_default libcma only Parameter value NULL.