This is the mail archive of the
libc-alpha@sourceware.org
mailing list for the glibc project.
[patch] pthread_mutex asserts on s390(x)
- From: Michael Matz <matz at suse dot de>
- To: libc-alpha at sources dot redhat dot com
- Cc: Martin Schwidefsky <martin dot schwidefsky at de dot ibm dot com>
- Date: Mon, 3 Nov 2008 13:18:58 +0100 (CET)
- Subject: [patch] pthread_mutex asserts on s390(x)
Hi,
the attached testcase (reduced from one in libapr) frequently triggers the
mutex->__data.__owner == 0 assert. It turns out the problem are missing
memory clobbers in the inline asm versions of atomic cmp-and-swap. As
they are also used to implement the low-level-locks they need to act as
barrier. With the patch the load is not reordered anymore and the
testcase survices.
Ciao,
Michael.
--
* sysdeps/s390/bits/atomic.h
(__arch_compare_and_exchange_val_32_acq,
__arch_compare_and_exchange_val_64_acq): Add "memory" clobber.
Index: sysdeps/s390/bits/atomic.h
===================================================================
--- sysdeps/s390/bits/atomic.h.orig
+++ sysdeps/s390/bits/atomic.h
@@ -56,7 +56,7 @@ typedef uintmax_t uatomic_max_t;
__typeof (*mem) __archold = (oldval); \
__asm __volatile ("cs %0,%2,%1" \
: "+d" (__archold), "=Q" (*__archmem) \
- : "d" (newval), "m" (*__archmem) : "cc" ); \
+ : "d" (newval), "m" (*__archmem) : "cc", "memory" ); \
__archold; })
#ifdef __s390x__
@@ -65,7 +65,7 @@ typedef uintmax_t uatomic_max_t;
__typeof (*mem) __archold = (oldval); \
__asm __volatile ("csg %0,%2,%1" \
: "+d" (__archold), "=Q" (*__archmem) \
- : "d" ((long) (newval)), "m" (*__archmem) : "cc" ); \
+ : "d" ((long) (newval)), "m" (*__archmem) : "cc", "memory" ); \
__archold; })
#else
/* For 31 bit we do not really need 64-bit compare-and-exchange. We can
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define MAX_COUNTER 1000000
#define MAX_THREADS 6
static long mutex_counter;
static pthread_mutex_t thread_lock;
void * thread_mutex_func(void *data);
int test_thread_mutex(int num_threads);
void * thread_mutex_func(void *data)
{
int i;
for (i = 0; i < MAX_COUNTER; i++) {
pthread_mutex_lock(&thread_lock);
mutex_counter++;
pthread_mutex_unlock(&thread_lock);
}
return NULL;
}
int test_thread_mutex_nested(int num_threads)
{
pthread_t t[MAX_THREADS];
int s[MAX_THREADS];
time_t time_start, time_stop;
int i, rv;
mutex_counter = 0;
printf("apr_thread_mutex_t Tests\n");
printf("%-60s", " Initializing the apr_thread_mutex_t (NESTED)");
pthread_mutexattr_t mattr;
rv = pthread_mutexattr_init(&mattr);
if (rv) return rv;
rv = pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE);
if (rv) {
pthread_mutexattr_destroy(&mattr);
return rv;
}
s[0] = pthread_mutex_init(&thread_lock, &mattr);
pthread_mutexattr_destroy(&mattr);
if (s[0] != 0) {
printf("Failed!\n");
return s[0];
}
printf("OK\n");
pthread_mutex_lock(&thread_lock);
printf(" Starting %d threads ", num_threads);
for (i = 0; i < num_threads; ++i) {
s[i] = pthread_create(&t[i], NULL, thread_mutex_func, NULL);
if (s[i] != 0) {
printf("Failed!\n");
return s[i];
}
}
printf("OK\n");
time_start = time(NULL);
pthread_mutex_unlock(&thread_lock);
/* printf("%-60s", " Waiting for threads to exit"); */
for (i = 0; i < num_threads; ++i) {
pthread_join(t[i], NULL);
}
/* printf("OK\n"); */
time_stop = time(NULL);
printf("seconds: %d usec\n",
(int)(time_stop - time_start));
if (mutex_counter != MAX_COUNTER * num_threads)
printf("error: counter = %ld\n", mutex_counter);
return 0;
}
int main(int argc, const char * const *argv)
{
int rv;
if ((rv = test_thread_mutex_nested(2)) != 0) {
fprintf(stderr,"thread_mutex (NESTED) test failed\n");
exit(-4);
}
return 0;
}