1 /*
2 * stress1.c
3 *
4 *
5 * --------------------------------------------------------------------------
6 *
7 * Pthreads-win32 - POSIX Threads Library for Win32
8 * Copyright(C) 1998 John E. Bossom
9 * Copyright(C) 1999,2005 Pthreads-win32 contributors
10 *
11 * Contact Email: rpj@callisto.canberra.edu.au
12 *
13 * The current list of contributors is contained
14 * in the file CONTRIBUTORS included with the source
15 * code distribution. The list can also be seen at the
16 * following World Wide Web location:
17 * http://sources.redhat.com/pthreads-win32/contributors.html
18 *
19 * This library is free software; you can redistribute it and/or
20 * modify it under the terms of the GNU Lesser General Public
21 * License as published by the Free Software Foundation; either
22 * version 2 of the License, or (at your option) any later version.
23 *
24 * This library is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 * Lesser General Public License for more details.
28 *
29 * You should have received a copy of the GNU Lesser General Public
30 * License along with this library in the file COPYING.LIB;
31 * if not, write to the Free Software Foundation, Inc.,
32 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
33 *
34 * --------------------------------------------------------------------------
35 *
36 * Test Synopsis:
37 * - Stress test condition variables, mutexes, semaphores.
38 *
39 * Test Method (Validation or Falsification):
40 * - Validation
41 *
42 * Requirements Tested:
43 * - Correct accounting of semaphore and condition variable waiters.
44 *
45 * Features Tested:
46 * -
47 *
48 * Cases Tested:
49 * -
50 *
51 * Description:
52 * Attempting to expose race conditions in cond vars, semaphores etc.
53 * - Master attempts to signal slave close to when timeout is due.
54 * - Master and slave do battle continuously until main tells them to stop.
55 * - Afterwards, the CV must be successfully destroyed (will return an
56 * error if there are waiters (including any internal semaphore waiters,
57 * which, if there are, cannot be real waiters).
58 *
59 * Environment:
60 * -
61 *
62 * Input:
63 * - None.
64 *
65 * Output:
66 * - File name, Line number, and failed expression on failure.
67 * - No output on success.
68 *
69 * Assumptions:
70 * -
71 *
72 * Pass Criteria:
73 * - CV is successfully destroyed.
74 *
75 * Fail Criteria:
76 * - CV destroy fails.
77 */
78
79 #include "test.h"
80 #include <string.h>
81 #include <sys/timeb.h>
82
83
84 const unsigned int ITERATIONS = 1000;
85
86 static pthread_t master, slave;
87 typedef struct {
88 int value;
89 pthread_cond_t cv;
90 pthread_mutex_t mx;
91 } mysig_t;
92
93 static int allExit;
94 static mysig_t control = {0, PTHREAD_COND_INITIALIZER, PTHREAD_MUTEX_INITIALIZER};
95 static pthread_barrier_t startBarrier, readyBarrier, holdBarrier;
96 static int timeoutCount = 0;
97 static int signalsTakenCount = 0;
98 static int signalsSent = 0;
99 static int bias = 0;
100 static int timeout = 10; // Must be > 0
101
102 enum {
103 CTL_STOP = -1
104 };
105
106 /*
107 * Returns abstime 'milliseconds' from 'now'.
108 *
109 * Works for: -INT_MAX <= millisecs <= INT_MAX
110 */
111 struct timespec *
millisecondsFromNow(struct timespec * time,int millisecs)112 millisecondsFromNow (struct timespec * time, int millisecs)
113 {
114 struct _timeb currSysTime;
115 int64_t nanosecs, secs;
116 const int64_t NANOSEC_PER_MILLISEC = 1000000;
117 const int64_t NANOSEC_PER_SEC = 1000000000;
118
119 /* get current system time and add millisecs */
120 _ftime(&currSysTime);
121
122 secs = (int64_t)(currSysTime.time) + (millisecs / 1000);
123 nanosecs = ((int64_t) (millisecs%1000 + currSysTime.millitm)) * NANOSEC_PER_MILLISEC;
124 if (nanosecs >= NANOSEC_PER_SEC)
125 {
126 secs++;
127 nanosecs -= NANOSEC_PER_SEC;
128 }
129 else if (nanosecs < 0)
130 {
131 secs--;
132 nanosecs += NANOSEC_PER_SEC;
133 }
134
135 time->tv_nsec = (long)nanosecs;
136 time->tv_sec = (long)secs;
137
138 return time;
139 }
140
141 void *
masterThread(void * arg)142 masterThread (void * arg)
143 {
144 int dither = (int) (size_t) arg;
145
146 timeout = (int) (size_t) arg;
147
148 pthread_barrier_wait(&startBarrier);
149
150 do
151 {
152 int sleepTime;
153
154 assert(pthread_mutex_lock(&control.mx) == 0);
155 control.value = timeout;
156 assert(pthread_mutex_unlock(&control.mx) == 0);
157
158 /*
159 * We are attempting to send the signal close to when the slave
160 * is due to timeout. We feel around by adding some [non-random] dither.
161 *
162 * dither is in the range 2*timeout peak-to-peak
163 * sleep time is the average of timeout plus dither.
164 * e.g.
165 * if timeout = 10 then dither = 20 and
166 * sleep millisecs is: 5 <= ms <= 15
167 *
168 * The bias value attempts to apply some negative feedback to keep
169 * the ratio of timeouts to signals taken close to 1:1.
170 * bias changes more slowly than dither so as to average more.
171 *
172 * Finally, if abs(bias) exceeds timeout then timeout is incremented.
173 */
174 if (signalsSent % timeout == 0)
175 {
176 if (timeoutCount > signalsTakenCount)
177 {
178 bias++;
179 }
180 else if (timeoutCount < signalsTakenCount)
181 {
182 bias--;
183 }
184 if (bias < -timeout || bias > timeout)
185 {
186 timeout++;
187 }
188 }
189 dither = (dither + 1 ) % (timeout * 2);
190 sleepTime = (timeout - bias + dither) / 2;
191 Sleep(sleepTime);
192 assert(pthread_cond_signal(&control.cv) == 0);
193 signalsSent++;
194
195 pthread_barrier_wait(&holdBarrier);
196 pthread_barrier_wait(&readyBarrier);
197 }
198 while (!allExit);
199
200 return NULL;
201 }
202
203 void *
slaveThread(void * arg)204 slaveThread (void * arg)
205 {
206 struct timespec time;
207
208 pthread_barrier_wait(&startBarrier);
209
210 do
211 {
212 assert(pthread_mutex_lock(&control.mx) == 0);
213 if (pthread_cond_timedwait(&control.cv,
214 &control.mx,
215 millisecondsFromNow(&time, control.value)) == ETIMEDOUT)
216 {
217 timeoutCount++;
218 }
219 else
220 {
221 signalsTakenCount++;
222 }
223 assert(pthread_mutex_unlock(&control.mx) == 0);
224
225 pthread_barrier_wait(&holdBarrier);
226 pthread_barrier_wait(&readyBarrier);
227 }
228 while (!allExit);
229
230 return NULL;
231 }
232
233 int
main()234 main ()
235 {
236 unsigned int i;
237
238 assert(pthread_barrier_init(&startBarrier, NULL, 3) == 0);
239 assert(pthread_barrier_init(&readyBarrier, NULL, 3) == 0);
240 assert(pthread_barrier_init(&holdBarrier, NULL, 3) == 0);
241
242 assert(pthread_create(&master, NULL, masterThread, (void *) (size_t) timeout) == 0);
243 assert(pthread_create(&slave, NULL, slaveThread, NULL) == 0);
244
245 allExit = FALSE;
246
247 pthread_barrier_wait(&startBarrier);
248
249 for (i = 1; !allExit; i++)
250 {
251 pthread_barrier_wait(&holdBarrier);
252 if (i >= ITERATIONS)
253 {
254 allExit = TRUE;
255 }
256 pthread_barrier_wait(&readyBarrier);
257 }
258
259 assert(pthread_join(slave, NULL) == 0);
260 assert(pthread_join(master, NULL) == 0);
261
262 printf("Signals sent = %d\nWait timeouts = %d\nSignals taken = %d\nBias = %d\nTimeout = %d\n",
263 signalsSent,
264 timeoutCount,
265 signalsTakenCount,
266 (int) bias,
267 timeout);
268
269 /* Cleanup */
270 assert(pthread_barrier_destroy(&holdBarrier) == 0);
271 assert(pthread_barrier_destroy(&readyBarrier) == 0);
272 assert(pthread_barrier_destroy(&startBarrier) == 0);
273 assert(pthread_cond_destroy(&control.cv) == 0);
274 assert(pthread_mutex_destroy(&control.mx) == 0);
275
276 /* Success. */
277 return 0;
278 }
279