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