WCSLIB: Thread-safety

WCSLIB 8.9

Loading...
Searching...
No Matches
Thread-safety

WCSLIB is thread-safe as long as the following provisos are observed:

- The WCSLIB structs - wcsprm, linprm, disprm, tabprm, spcprm, celprm, and prjprm - contain parameter values that must be provided by the user and intermediate values that are computed from them by the setup routines - wcsset(), linset(), disset(), tabset(), spcset(), celset(), and prjset().

  Once the structs have been set up they essentially become read-only objects as far as the WCSLIB routines are concerned (except if wcserr is used, see below). This must be done once and for all before threaded execution commences. Note that the setup routines themselves are not thread-safe unless the struct has been put into "bypass" mode.

  Thus, if, for some reason, calls to a setup routine, for example wcsset(), can't be avoided during threaded execution, then the struct must be put into bypass mode. This is done by setting the flag member to 1, e.g. wcsprm::flag = 1 (rather than 0) before calling wcsset(). Then, after setting up the struct, wcsset() will reset the flag to a special value that causes it to return immediately if reinvoked on that struct.

  To take a struct out of bypass mode, simply reset the flag member to zero. This, of course, must be done during non-threaded execution. See also wcsenq().

- If wcserr is enabled, its usage is only thread-safe if WCSLIB is compiled with C11 or later. C11 provides a simple spinlock via the stdatomic functions. Without it, two threads may attempt simultaneously to operate on the shared wcserr struct, i.e. to set, clear, or copy it, leading to race conditions and potentially segfaults.

  wcserr_enable() itself sets a static variable and so is not thread-safe. However, the error reporting facility is not intended to be used dynamically. If detailed error messages are required, enable wcserr when execution starts and don't change it.

- wcsprintf uses thread-local storage (TLS) for its module static variables when output is directed to an internal buffer, as when wcsprintf_set() is called with a NULL pointer. That is, messages are collected by each thread in its own internal string buffer. The contents of the buffer must be used, e.g. printed to stdout, before the thread terminates, at which time the buffer is deleted.

- The low-level routines wcsnpv(), wcsnps(), and disndp() are not thread-safe, but they are not used within WCSLIB itself other than to get (not set) the values of the global variables NPVMAX, NPSMAX, and NDPMAX.

  wcsinit(), lininit, lindist(), and disinit() only do so to get default values if the relevant parameters are not provided as function arguments. Note that wcsini() invokes wcsinit() with defaults that cause this behavior, as does linini() invoking lininit(), lindis() invoking lindist(), and disini() invoking disinit().

  The preset values of NPVMAX(=64), NPSMAX(=8), and NDPMAX(=256) are large enough to cover most practical cases. However, it may be desirable to tailor them to avoid allocating memory that remains unused. If so, and thread-safety is an issue, then use wcsinit() and disinit() instead with the relevant values specified. This is what WCSLIB routines, such as the header parsers wcspih() and wcsbth(), do to avoid wasting memory.

- The Flex scanners were made reentrant at release 5.18 and consequently should be thread-safe. This was achieved by rewriting them as thin wrappers (with the same API) over scanners that were modified (with changed API), as required to use Flex's "reentrant" option.

Note that diagnostic routines that print the contents of the various structs, namely celprt(), disprt(), linprt(), prjprt(), spcprt(), tabprt(), wcsprt(), and wcsperr() use printf() which is thread-safe by the POSIX requirement on `stdio`. However, this is only at the function level. Where multiple threads invoke these routines simultaneously their output is likely to be interleaved.