| #include <pthread.h> |
| #include <signal.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <sys/mman.h> |
| #include <sys/wait.h> |
| |
| |
| |
| |
| static void prepare (void); |
| #define PREPARE(argc, argv) prepare () |
| static int do_test (void); |
| #define TEST_FUNCTION do_test () |
| #define TIMEOUT 5 |
| #include "../test-skeleton.c" |
| |
| |
| static int fd; |
| #define N 100 |
| |
| static void |
| prepare (void) |
| { |
| fd = create_temp_file ("tst-robust8", NULL); |
| if (fd == -1) |
| exit (1); |
| } |
| |
| |
| #define THESIGNAL SIGKILL |
| #define ROUNDS 5 |
| #define THREADS 9 |
| |
| |
| static const struct timespec before = { 0, 0 }; |
| |
| |
| static pthread_mutex_t *map; |
| |
| |
| static void * |
| tf (void *arg) |
| { |
| long int nr = (long int) arg; |
| int fct = nr % 3; |
| |
| uint8_t state[N]; |
| memset (state, '\0', sizeof (state)); |
| |
| while (1) |
| { |
| int r = random () % N; |
| if (state[r] == 0) |
| { |
| int e; |
| |
| switch (fct) |
| { |
| case 0: |
| e = pthread_mutex_lock (&map[r]); |
| if (e != 0) |
| { |
| printf ("mutex_lock of %d in thread %ld failed with %d\n", |
| r, nr, e); |
| exit (1); |
| } |
| state[r] = 1; |
| break; |
| case 1: |
| e = pthread_mutex_timedlock (&map[r], &before); |
| if (e != 0 && e != ETIMEDOUT) |
| { |
| printf ("\ |
| mutex_timedlock of %d in thread %ld failed with %d\n", |
| r, nr, e); |
| exit (1); |
| } |
| break; |
| default: |
| e = pthread_mutex_trylock (&map[r]); |
| if (e != 0 && e != EBUSY) |
| { |
| printf ("mutex_trylock of %d in thread %ld failed with %d\n", |
| r, nr, e); |
| exit (1); |
| } |
| break; |
| } |
| |
| if (e == EOWNERDEAD) |
| pthread_mutex_consistent_np (&map[r]); |
| |
| if (e == 0 || e == EOWNERDEAD) |
| state[r] = 1; |
| } |
| else |
| { |
| int e = pthread_mutex_unlock (&map[r]); |
| if (e != 0) |
| { |
| printf ("mutex_unlock of %d in thread %ld failed with %d\n", |
| r, nr, e); |
| exit (1); |
| } |
| |
| state[r] = 0; |
| } |
| } |
| } |
| |
| |
| static void |
| child (int round) |
| { |
| for (int thread = 1; thread <= THREADS; ++thread) |
| { |
| pthread_t th; |
| if (pthread_create (&th, NULL, tf, (void *) (long int) thread) != 0) |
| { |
| printf ("cannot create thread %d in round %d\n", thread, round); |
| exit (1); |
| } |
| } |
| |
| struct timespec ts; |
| ts.tv_sec = 0; |
| ts.tv_nsec = 1000000000 / ROUNDS; |
| while (nanosleep (&ts, &ts) != 0) |
| /* nothing */; |
| |
| /* Time to die. */ |
| kill (getpid (), THESIGNAL); |
| |
| /* We better never get here. */ |
| abort (); |
| } |
| |
| |
| static int |
| do_test (void) |
| { |
| if (ftruncate (fd, N * sizeof (pthread_mutex_t)) != 0) |
| { |
| puts ("cannot size new file"); |
| return 1; |
| } |
| |
| map = mmap (NULL, N * sizeof (pthread_mutex_t), PROT_READ | PROT_WRITE, |
| MAP_SHARED, fd, 0); |
| if (map == MAP_FAILED) |
| { |
| puts ("mapping failed"); |
| return 1; |
| } |
| |
| pthread_mutexattr_t ma; |
| if (pthread_mutexattr_init (&ma) != 0) |
| { |
| puts ("mutexattr_init failed"); |
| return 0; |
| } |
| if (pthread_mutexattr_setrobust_np (&ma, PTHREAD_MUTEX_ROBUST_NP) != 0) |
| { |
| puts ("mutexattr_setrobust failed"); |
| return 1; |
| } |
| if (pthread_mutexattr_setpshared (&ma, PTHREAD_PROCESS_SHARED) != 0) |
| { |
| puts ("mutexattr_setpshared failed"); |
| return 1; |
| } |
| #ifdef ENABLE_PI |
| if (pthread_mutexattr_setprotocol (&ma, PTHREAD_PRIO_INHERIT) != 0) |
| { |
| puts ("pthread_mutexattr_setprotocol failed"); |
| return 1; |
| } |
| #endif |
| |
| for (int round = 1; round <= ROUNDS; ++round) |
| { |
| for (int n = 0; n < N; ++n) |
| { |
| int e = pthread_mutex_init (&map[n], &ma); |
| if (e == ENOTSUP) |
| { |
| #ifdef ENABLE_PI |
| puts ("cannot support pshared robust PI mutexes"); |
| #else |
| puts ("cannot support pshared robust mutexes"); |
| #endif |
| return 0; |
| } |
| if (e != 0) |
| { |
| printf ("mutex_init %d in round %d failed\n", n + 1, round); |
| return 1; |
| } |
| } |
| |
| pid_t p = fork (); |
| if (p == -1) |
| { |
| printf ("fork in round %d failed\n", round); |
| return 1; |
| } |
| if (p == 0) |
| child (round); |
| |
| int status; |
| if (TEMP_FAILURE_RETRY (waitpid (p, &status, 0)) != p) |
| { |
| printf ("waitpid in round %d failed\n", round); |
| return 1; |
| } |
| if (!WIFSIGNALED (status)) |
| { |
| printf ("child did not die of a signal in round %d\n", round); |
| return 1; |
| } |
| if (WTERMSIG (status) != THESIGNAL) |
| { |
| printf ("child did not die of signal %d in round %d\n", |
| THESIGNAL, round); |
| return 1; |
| } |
| |
| for (int n = 0; n < N; ++n) |
| { |
| int e = pthread_mutex_lock (&map[n]); |
| if (e != 0 && e != EOWNERDEAD) |
| { |
| printf ("mutex_lock %d failed in round %d\n", n + 1, round); |
| return 1; |
| } |
| } |
| |
| for (int n = 0; n < N; ++n) |
| if (pthread_mutex_unlock (&map[n]) != 0) |
| { |
| printf ("mutex_unlock %d failed in round %d\n", n + 1, round); |
| return 1; |
| } |
| |
| for (int n = 0; n < N; ++n) |
| { |
| int e = pthread_mutex_destroy (&map[n]); |
| if (e != 0) |
| { |
| printf ("mutex_destroy %d in round %d failed with %d\n", |
| n + 1, round, e); |
| printf("nusers = %d\n", (int) map[n].__data.__nusers); |
| return 1; |
| } |
| } |
| } |
| |
| if (pthread_mutexattr_destroy (&ma) != 0) |
| { |
| puts ("mutexattr_destroy failed"); |
| return 1; |
| } |
| |
| if (munmap (map, N * sizeof (pthread_mutex_t)) != 0) |
| { |
| puts ("munmap failed"); |
| return 1; |
| } |
| |
| return 0; |
| } |