1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
|
/* Check that recycling thread slots doesn't cause new threads to
inherit the disablement status of the previous thread to occupy
that slot.
1. Create N threads, disable error reporting in them, and get them
all to exit (join with them). That creates N thread slots that
were vacated by threads with error reporting disabled. There
should be N complaints about threads exiting with errors
disabled.
2. Create N new threads and get them to wait at a barrier.
3. Let them all go past the barrier and call err(). There
should be N resulting error reports.
4. Join with the N threads.
*/
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <pthread.h>
#include <semaphore.h>
#include <limits.h> /* PTHREAD_STACK_MIN */
#include "../include/valgrind.h"
char* block = NULL;
# if !defined(VGO_darwin)
sem_t sem;
# else
sem_t *sem;
static const char *semname = "Semaphore1";
# endif
__attribute__((noinline)) void usechar ( char c )
{
// Spook gcc into believing mysterious bad things are
// happening behind its back, and that 'c' is definitely
// used in some (unknown) way.
__asm__ __volatile__("" : : "r"(c) : "memory","cc");
}
__attribute__((noinline)) void err ( void )
{
usechar( block[5] );
}
void* child_fn_1 ( void* arg )
{
// Disable error reporting, then wait to exit
VALGRIND_DISABLE_ERROR_REPORTING;
# if !defined(VGO_darwin)
int r = sem_wait(&sem); assert(!r);
# else
int r = sem_wait(sem); assert(!r);
# endif
return NULL;
}
void* child_fn_2 ( void* arg )
{
// make an error, then wait to exit
err();
# if !defined(VGO_darwin)
int r = sem_wait(&sem); assert(!r);
# else
int r = sem_wait(sem); assert(!r);
# endif
return NULL;
}
#define NTHREADS 498 // VG_N_THREADS - 2
int main ( void )
{
int r, i;
pthread_t child[NTHREADS];
block = malloc(10);
free(block);
// part 1
fprintf(stderr, "\n-------- Letting %d threads exit "
"w/ errs disabled ------\n\n",
NTHREADS);
// set up the semaphore
# if !defined(VGO_darwin)
r = sem_init(&sem, 0, 0); assert(!r);
# else
sem = sem_open(semname, O_CREAT, 0777, 0); assert(!(sem == SEM_FAILED));
# endif
pthread_attr_t attr;
r = pthread_attr_init(&attr); assert(!r);
r = pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN);
// create N threads to do child_fn_1 ...
for (i = 0; i < NTHREADS; i++) {
r = pthread_create(&child[i], &attr, child_fn_1, NULL);
assert(!r);
}
// let them all exit
for (i = 0; i < NTHREADS; i++) {
# if !defined(VGO_darwin)
r = sem_post(&sem); assert(!r);
# else
r = sem_post(sem); assert(!r);
# endif
}
// join
for (i = 0; i < NTHREADS; i++) {
r = pthread_join(child[i], NULL); assert(!r);
}
// part 2
fprintf(stderr, "\n-------- Letting %d threads make an error "
"------\n\n",
NTHREADS);
// semaphore is already back at zero
// create N threads to do child_fn_2 ...
for (i = 0; i < NTHREADS; i++) {
r = pthread_create(&child[i], &attr, child_fn_2, NULL);
assert(!r);
}
// let them all exit
for (i = 0; i < NTHREADS; i++) {
# if !defined(VGO_darwin)
r = sem_post(&sem); assert(!r);
# else
r = sem_post(sem); assert(!r);
# endif
}
// join
for (i = 0; i < NTHREADS; i++) {
r = pthread_join(child[i], NULL); assert(!r);
}
// Print the final error counts. There need to be 498 errors
// in 1 context. Anything else, and something is not right.
int nerrors = VALGRIND_COUNT_ERRORS;
fprintf(stderr, "\n-------- Got %d errors (expected %d ==> %s) ------\n\n",
nerrors, NTHREADS, nerrors == NTHREADS ? "PASS" : "FAIL" );
return 0;
}
|