Multi-Threaded Applications Rules for Linux

Each thread of a multi-threaded application shares common global and static data space. As a result, designers of multi-threaded programs have to be careful at design time to avoid reading and writing global variables, or to carefully structure their code to coordinate global variable access. The Software Group's X.25 library uses global variables in some circumstances; programmers needing to use the library with multi-threaded applications need to be aware of the implications of uncontrolled access to certain data elements within the library.

The Primary Rule

The library maintains relevant information for each virtual circuit in an internal data structure of type CALLT. X25open() allocates the data structure and x25close() releases it back to the operating system. As this is an internal, undocumented structure, applications do not refer to any CALLT elements directly. The library provides function calls and macros for this.

The library does not enforce mutual exclusion on CALLT data structures. As a result, the primary rule of writing multi-threaded applications using the X.25 library API is that "Each virtual circuit must be accessed by only one thread." If the programmer composes a program with a reader thread (to collect information from a virtual circuit) and a writer thread (to provide information to that virtual circuit), the application will break.

Designing applications where there is a one-to-one correspondence between threads and virtual circuits (i.e. each thread controls a single virtual circuit) is a valid approach.. There is however no restriction to the number of virtual circuits that a single thread can control (multiple CALLTs), including the use of X25looksel() to monitor those circuits.

Global Variables

The library maintains several global variables to control its operation. Therefore, some of operations performed by one thread affect all others.

The tag T_VERBOSE sets a global variable that all library routines check during operation. When set, routines print status messages continuously to the error output stream. T_VERBOSE is a debugging aid which is never used in production systems. The last thread to manipulate this tag affects the operation of all other threads making library calls.

The tag T_OUTERR defines the file stream which receives error messages whenever a thread calls X25error() (and where other library routines send status information if verbose mode is on). All threads share this error output stream.

The library often returns error codes in a global variable, x25errno, as indicated in the description of the library functions. In a multi-threaded environment, checking x25errno does not guarantee that a thread is referencing the error code related to the last operation it performed. Multi-threaded applications need to call X25geterrorct(). Applications should never use x25errno. x25errno is now defined to be a call to X25geterror(). Note that there are exceptions to this rule.

Single-Threaded Routines

Do not use the library routines X25sethostent(), X25gethostent(), X25gethostopts() and X25endhostent() from multi-threaded applications. Instead, multi-threaded application developers should use X25gethostbyaddr() and X25gethostbyname() (which also access the information from /etc/x25hosts).

Similarly, the library routines X25setnetent(), X25getnetent() and X25endnetent() are restricted to single-threaded applications while the routines X25getnetbyname(), X25getnetbynetid() and X25getnetbyboard() may be used in multi-threaded applications.

The routine X25error() may only be used in single-threaded applications as it interprets the internal X.25 internal global error variable. In multi-threaded applications, the replacement routine is:

void X25errorct(CALLT *ct, char *string)

which will interpret current error information from the CALLT structure.