79 lines
3.1 KiB
C
79 lines
3.1 KiB
C
#include_next <internaltypes.h>
|
|
#ifndef _INTERNAL_TYPES_H_HPPA_
|
|
#define _INTERNAL_TYPES_H_HPPA_ 1
|
|
#include <atomic.h>
|
|
|
|
/* In GLIBC 2.10 HPPA switched from Linuxthreads to NPTL, and in order
|
|
to maintain ABI compatibility with pthread_cond_t, some care had to be
|
|
taken.
|
|
|
|
The NPTL pthread_cond_t grew in size. When HPPA switched to NPTL, we
|
|
dropped the use of ldcw, and switched to the kernel helper routine for
|
|
compare-and-swap. This allowed HPPA to use the 4-word 16-byte aligned
|
|
lock words, and alignment words to store the additional pthread_cond_t
|
|
data. Once organized properly the new NPTL pthread_cond_t was 1 word
|
|
smaller than the Linuxthreads version.
|
|
|
|
However, we were faced with the case that users may have initialized the
|
|
pthread_cond_t with PTHREAD_COND_INITIALIZER. In this case, the first
|
|
four words were set to one, and must be cleared before any NPTL code
|
|
used these words.
|
|
|
|
We didn't want to use LDCW, because it continues to be a source of bugs
|
|
when applications memset pthread_cond_t to all zeroes by accident. This
|
|
works on all other architectures where lock words are unlocked at zero.
|
|
Remember that because of the semantics of LDCW, a locked word is set to
|
|
zero, and an unlocked word is set to 1.
|
|
|
|
Instead we used atomic_compare_and_exchange_val_acq, but we couldn't use
|
|
this on any of the pthread_cond_t words, otherwise it might interfere
|
|
with the current operation of the structure. To solve this problem we
|
|
used the left over word.
|
|
|
|
If the stucture was initialized by a legacy Linuxthread
|
|
PTHREAD_COND_INITIALIZER it contained a 1, and this indicates that the
|
|
structure requires zeroing for NPTL. The first thread to come upon a
|
|
pthread_cond_t with a 1 in the __initializer field, will
|
|
compare-and-swap the value, placing a 2 there which will cause all other
|
|
threads using the same pthread_cond_t to wait for the completion of the
|
|
initialization. Lastly, we use a store (with memory barrier) to change
|
|
__initializer from 2 to 0. Note that the store is strongly ordered, but
|
|
we use the PA 1.1 compatible form which is ",ma" with zero offset.
|
|
|
|
In the future, when the application is recompiled with NPTL
|
|
PTHREAD_COND_INITIALIZER it will be a quick compare-and-swap, which
|
|
fails because __initializer is zero, and the structure will be used as
|
|
is correctly. */
|
|
|
|
#define cond_compat_clear(var) \
|
|
({ \
|
|
int tmp = 0; \
|
|
var->__data.__lock = 0; \
|
|
var->__data.__futex = 0; \
|
|
var->__data.__mutex = NULL; \
|
|
/* Clear __initializer last, to indicate initialization is done. */ \
|
|
__asm__ __volatile__ ("stw,ma %1,0(%0)" \
|
|
: : "r" (&var->__data.__initializer), "r" (tmp) : "memory"); \
|
|
})
|
|
|
|
#define cond_compat_check_and_clear(var) \
|
|
({ \
|
|
int ret; \
|
|
volatile int *value = &var->__data.__initializer; \
|
|
if ((ret = atomic_compare_and_exchange_val_acq(value, 2, 1))) \
|
|
{ \
|
|
if (ret == 1) \
|
|
{ \
|
|
/* Initialize structure. */ \
|
|
cond_compat_clear (var); \
|
|
} \
|
|
else \
|
|
{ \
|
|
/* Yield until structure is initialized. */ \
|
|
while (*value == 2) sched_yield (); \
|
|
} \
|
|
} \
|
|
})
|
|
|
|
#endif
|