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
28char* block = NULL;
29sem_t sem;
30
31__attribute__((noinline)) void usechar ( char c )
32{
33   // Spook gcc into believing mysterious bad things are
34   // happening behind its back, and that 'c' is definitely
35   // used in some (unknown) way.
36   __asm__ __volatile__("" : : "r"(c) : "memory","cc");
37}
38
39__attribute__((noinline)) void err ( void )
40{
41   usechar( block[5] );
42}
43
44void* child_fn_1 ( void* arg )
45{
46   // Disable error reporting, then wait to exit
47   VALGRIND_DISABLE_ERROR_REPORTING;
48   int r = sem_wait(&sem);  assert(!r);
49   return NULL;
50}
51
52void* child_fn_2 ( void* arg )
53{
54   // make an error, then wait to exit
55   err();
56   int r = sem_wait(&sem);  assert(!r);
57   return NULL;
58}
59
60#define NTHREADS 498 // VG_N_THREADS - 2
61
62int main ( void )
63{
64  int r, i;
65  pthread_t child[NTHREADS];
66
67  block = malloc(10);
68  free(block);
69
70  // part 1
71  fprintf(stderr, "\n-------- Letting %d threads exit "
72                  "w/ errs disabled ------\n\n",
73          NTHREADS);
74
75  // set up the semaphore
76  r = sem_init(&sem, 0, 0);  assert(!r);
77
78  pthread_attr_t attr;
79  r = pthread_attr_init(&attr); assert(!r);
80  r = pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN);
81
82  // create N threads to do child_fn_1 ...
83  for (i = 0; i < NTHREADS; i++) {
84     r = pthread_create(&child[i], &attr, child_fn_1, NULL);
85     assert(!r);
86  }
87
88  // let them all exit
89  for (i = 0; i < NTHREADS; i++) {
90     r = sem_post(&sem);  assert(!r);
91  }
92
93  // join
94  for (i = 0; i < NTHREADS; i++) {
95     r = pthread_join(child[i], NULL);  assert(!r);
96  }
97
98  // part 2
99
100  fprintf(stderr, "\n-------- Letting %d threads make an error "
101                  "------\n\n",
102          NTHREADS);
103  // semaphore is already back at zero
104
105  // create N threads to do child_fn_2 ...
106  for (i = 0; i < NTHREADS; i++) {
107     r = pthread_create(&child[i], &attr, child_fn_2, NULL);
108     assert(!r);
109  }
110
111  // let them all exit
112  for (i = 0; i < NTHREADS; i++) {
113     r = sem_post(&sem);  assert(!r);
114  }
115
116  // join
117  for (i = 0; i < NTHREADS; i++) {
118     r = pthread_join(child[i], NULL);  assert(!r);
119  }
120
121  // Print the final error counts.  There need to be 498 errors
122  // in 1 context.  Anything else, and something is not right.
123  int nerrors = VALGRIND_COUNT_ERRORS;
124  fprintf(stderr, "\n-------- Got %d errors (expected %d ==> %s) ------\n\n",
125          nerrors, NTHREADS, nerrors == NTHREADS ? "PASS" : "FAIL" );
126
127  return 0;
128}
129