1 /* ``Licensed under the Apache License, Version 2.0 (the "License");
2 * you may not use this file except in compliance with the License.
3 * You may obtain a copy of the License at
4 *
5 * http://www.apache.org/licenses/LICENSE-2.0
6 *
7 * Unless required by applicable law or agreed to in writing, software
8 * distributed under the License is distributed on an "AS IS" BASIS,
9 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 * See the License for the specific language governing permissions and
11 * limitations under the License.
12 *
13 * The Initial Developer of the Original Code is Ericsson Utvecklings AB.
14 * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
15 * AB. All Rights Reserved.''
16 *
17 * $Id$
18 */
19
20
21 #ifndef __WIN32__
22 #include <sys/types.h>
23 #include <unistd.h>
24 #include <errno.h>
25 #endif
26 #include <stdio.h>
27 #include <stdarg.h>
28 #include <string.h>
29 #include "testcase_driver.h"
30 #include "allocator_test.h"
31
32 #ifdef __WIN32__
33 #undef HAVE_VSNPRINTF
34 #define HAVE_VSNPRINTF 1
35 #define vsnprintf _vsnprintf
36 #endif
37
38 #ifndef HAVE_VSNPRINTF
39 #define HAVE_VSNPRINTF 0
40 #endif
41
42 #define NO_OF_ALLOC_SEQS 6
43 #define NO_OF_THREADS (18)
44 #define NO_OF_BLOCKS 10
45 #define NO_OF_OPS_PER_BL 200
46 #define SBC_THRESHOLD 8192
47
48 #define BLOCK_ID(TN, BN) ((TN) << 4 | (BN))
49
50 #define ERR_BUF_SZ 4096
51 static char err_buf[ERR_BUF_SZ];
52 static volatile int tc_failed;
53 static int dead_thread_no;
54 static erts_mutex tc_mutex;
55 static erts_cond tc_cond;
56
exit_thread(int t_no,int do_lock)57 static void exit_thread(int t_no, int do_lock)
58 {
59 if (do_lock)
60 THR_MTX_LOCK(tc_mutex);
61
62 while (dead_thread_no >= 0)
63 THR_COND_WAIT(tc_cond, tc_mutex);
64 dead_thread_no = t_no;
65
66 THR_COND_BCAST(tc_cond);
67 THR_MTX_UNLOCK(tc_mutex);
68 THR_EXIT(NULL);
69 }
70
fail(int t_no,char * frmt,...)71 static void fail(int t_no, char *frmt, ...)
72 {
73 char buf[10];
74 size_t bufsz = sizeof(buf);
75 va_list va;
76
77 THR_MTX_LOCK(tc_mutex);
78
79 va_start(va, frmt);
80 #if HAVE_VSNPRINTF
81 vsnprintf(err_buf, ERR_BUF_SZ, frmt, va);
82 #else
83 vsprintf(err_buf, frmt, va);
84 #endif
85 va_end(va);
86
87 tc_failed = 1;
88
89 if (enif_getenv("ERL_ABORT_ON_FAILURE", buf, &bufsz) == 0
90 && strcmp("true", buf) == 0) {
91 fprintf(stderr, "Testcase \"%s\" failed: %s\n",
92 testcase_name(), err_buf);
93 abort();
94 }
95
96 exit_thread(t_no, 0);
97 }
98
99 static Allctr_t *alloc_ts_1 = NULL;
100 static Allctr_t *alloc_ts_2 = NULL;
101
stop_allocators(void)102 static void stop_allocators(void)
103 {
104 if (alloc_ts_1) {
105 STOP_ALC(alloc_ts_1);
106 alloc_ts_1 = NULL;
107 }
108 if (alloc_ts_2) {
109 STOP_ALC(alloc_ts_2);
110 alloc_ts_2 = NULL;
111 }
112 }
113
114
115 void *thread_func(void *arg);
116
117 typedef struct {
118 Allctr_t *a;
119 int t_no;
120 int no_ops_per_bl;
121 } ThreadData;
122
123
124 char *
testcase_name(void)125 testcase_name(void)
126 {
127 return "threads";
128 }
129
130 void
testcase_cleanup(TestCaseState_t * tcs)131 testcase_cleanup(TestCaseState_t *tcs)
132 {
133 stop_allocators();
134 }
135
136 void
testcase_run(TestCaseState_t * tcs)137 testcase_run(TestCaseState_t *tcs)
138 {
139 struct {
140 erts_thread tid;
141 ThreadData arg;
142 } threads[NO_OF_THREADS+1] = {{0}};
143 int no_threads;
144 int i;
145 char sbct_buf[10];
146 char *argv_org[] = {"-tasaobf", "-tmmsbc5000", "-tmmmbc5000", "-tsbct",
147 &sbct_buf[0], NULL};
148 char *argv[sizeof(argv_org)/sizeof(argv_org[0])];
149
150 if (!IS_THREADS_ENABLED)
151 testcase_skipped(tcs, "Threads not enabled");
152
153 alloc_ts_1 = NULL;
154 alloc_ts_2 = NULL;
155
156 err_buf[0] = '\0';
157
158 sprintf(sbct_buf, "%d", SBC_THRESHOLD/1024);
159
160 memcpy((void *) argv, argv_org, sizeof(argv_org));
161 alloc_ts_1 = START_ALC("threads_ts_1", 1, argv);
162 ASSERT(tcs, alloc_ts_1);
163 memcpy((void *) argv, argv_org, sizeof(argv_org));
164 alloc_ts_2 = START_ALC("threads_ts_2", 1, argv);
165 ASSERT(tcs, alloc_ts_2);
166
167 ASSERT(tcs, IS_ALLOC_THREAD_SAFE(alloc_ts_1));
168 ASSERT(tcs, IS_ALLOC_THREAD_SAFE(alloc_ts_2));
169
170 tc_mutex = THR_MTX_CREATE();
171 tc_cond = THR_COND_CREATE();
172
173 THR_MTX_LOCK(tc_mutex);
174
175 dead_thread_no = -1;
176 no_threads = 0;
177
178 for(i = 1; i <= NO_OF_THREADS; i++) {
179 char *alc;
180
181 threads[i].arg.no_ops_per_bl = NO_OF_OPS_PER_BL;
182
183 if (i % 2 == 0) {
184 alc = "threads_ts_1";
185 threads[i].arg.a = alloc_ts_1;
186 }
187 else {
188 alc = "threads_ts_2";
189 threads[i].arg.a = alloc_ts_2;
190 }
191 threads[i].arg.t_no = i;
192
193 threads[i].tid = THR_CREATE(thread_func, (void *) &threads[i].arg);
194 if (threads[i].tid) {
195 testcase_printf(tcs, "Successfully created thread %d "
196 "using %s_alloc\n", i, alc);
197 no_threads++;
198 }
199 else {
200 tc_failed = 1;
201 sprintf(err_buf, "Failed to create thread %d\n", i);
202 break;
203 }
204
205 }
206
207 while (no_threads) {
208 THR_COND_WAIT(tc_cond, tc_mutex);
209 if (dead_thread_no >= 0) {
210 no_threads--;
211 THR_JOIN(threads[dead_thread_no].tid);
212 testcase_printf(tcs, "Thread %d died\n", dead_thread_no);
213 dead_thread_no = -1;
214 THR_COND_BCAST(tc_cond);
215 }
216 }
217
218 THR_MTX_UNLOCK(tc_mutex);
219 THR_MTX_DESTROY(tc_mutex);
220 THR_COND_DESTROY(tc_cond);
221
222 stop_allocators();
223
224 if (tc_failed)
225 testcase_failed(tcs, "%s", err_buf);
226 }
227
228 Ulong alloc_seq_1[] = {
229 17,
230 SBC_THRESHOLD*2,
231 SBC_THRESHOLD*20,
232 SBC_THRESHOLD*2,
233 17,
234 0
235 };
236
237 Ulong alloc_seq_2[] = {
238 SBC_THRESHOLD*20,
239 SBC_THRESHOLD*2,
240 17,
241 SBC_THRESHOLD*2,
242 SBC_THRESHOLD*20,
243 0
244 };
245
246 Ulong alloc_seq_3[] = {
247 1,
248 SBC_THRESHOLD/10,
249 SBC_THRESHOLD/9,
250 SBC_THRESHOLD/8,
251 SBC_THRESHOLD/7,
252 SBC_THRESHOLD/6,
253 SBC_THRESHOLD/5,
254 SBC_THRESHOLD/4,
255 SBC_THRESHOLD/3,
256 SBC_THRESHOLD/2,
257 SBC_THRESHOLD/1,
258 SBC_THRESHOLD*1,
259 SBC_THRESHOLD*2,
260 SBC_THRESHOLD*3,
261 SBC_THRESHOLD*4,
262 SBC_THRESHOLD*5,
263 SBC_THRESHOLD*6,
264 SBC_THRESHOLD*7,
265 SBC_THRESHOLD*8,
266 SBC_THRESHOLD*9,
267 SBC_THRESHOLD*10,
268 SBC_THRESHOLD*9,
269 SBC_THRESHOLD*8,
270 SBC_THRESHOLD*7,
271 SBC_THRESHOLD*6,
272 SBC_THRESHOLD*5,
273 SBC_THRESHOLD*4,
274 SBC_THRESHOLD*3,
275 SBC_THRESHOLD*2,
276 SBC_THRESHOLD*1,
277 SBC_THRESHOLD/2,
278 SBC_THRESHOLD/3,
279 SBC_THRESHOLD/4,
280 SBC_THRESHOLD/5,
281 SBC_THRESHOLD/6,
282 SBC_THRESHOLD/7,
283 SBC_THRESHOLD/8,
284 SBC_THRESHOLD/9,
285 SBC_THRESHOLD/10,
286 1,
287 0
288 };
289
290 Ulong alloc_seq_4[] = {
291 SBC_THRESHOLD*2,
292 SBC_THRESHOLD*3,
293 SBC_THRESHOLD*7,
294 SBC_THRESHOLD*8,
295 SBC_THRESHOLD*5,
296 SBC_THRESHOLD*6,
297 SBC_THRESHOLD*1,
298 SBC_THRESHOLD*10,
299 SBC_THRESHOLD*4,
300 SBC_THRESHOLD*2,
301 0
302 };
303
304 Ulong alloc_seq_5[] = {
305 SBC_THRESHOLD/2,
306 SBC_THRESHOLD/3,
307 SBC_THRESHOLD/7,
308 SBC_THRESHOLD/8,
309 SBC_THRESHOLD/5,
310 SBC_THRESHOLD/6,
311 SBC_THRESHOLD/1,
312 SBC_THRESHOLD/10,
313 SBC_THRESHOLD/4,
314 SBC_THRESHOLD/2,
315 SBC_THRESHOLD/3,
316 SBC_THRESHOLD/7,
317 SBC_THRESHOLD/8,
318 SBC_THRESHOLD/5,
319 SBC_THRESHOLD/6,
320 SBC_THRESHOLD/1,
321 SBC_THRESHOLD/10,
322 SBC_THRESHOLD/4,
323 SBC_THRESHOLD/2,
324 0
325 };
326
327 Ulong alloc_seq_6[] = {
328 1, 50, 100, 50, 23, 46, 2345, 23, 54, 2, 0
329 };
330
331 Ulong *alloc_seqs[NO_OF_ALLOC_SEQS] = {
332 alloc_seq_1,
333 alloc_seq_2,
334 alloc_seq_3,
335 alloc_seq_4,
336 alloc_seq_5,
337 alloc_seq_6
338 };
339
340 typedef struct {
341 unsigned char *p;
342 Ulong s;
343 int i;
344 Ulong *as;
345 } block;
346
347 #define CHECK_BLOCK_DATA(T, P, S, D) \
348 check_block_data(__FILE__, __LINE__, (T), (P), (S), (D))
349
350 static void
check_block_data(char * file,int line,int t_no,unsigned char * p,Ulong sz,int d)351 check_block_data(char *file, int line, int t_no,
352 unsigned char *p, Ulong sz, int d)
353 {
354 Ulong i;
355 for (i = 0; i < sz; i++)
356 if (p[i] != (unsigned char) d)
357 fail(t_no, "%s:%d: Thread no %d found clobbered data! "
358 "found id=%d; expected id=%d\n",
359 file, line, t_no, (int) p[i], d);
360 }
361
362 static void
alloc_op(int t_no,Allctr_t * a,block * bp,int id,int clean_up)363 alloc_op(int t_no, Allctr_t *a, block *bp, int id, int clean_up)
364 {
365 if (tc_failed)
366 exit_thread(t_no, 1);
367
368 if(bp->p)
369 CHECK_BLOCK_DATA(t_no, bp->p, bp->s, id);
370
371 if(bp->as[bp->i] == 0 || clean_up) {
372 FREE(a, bp->p);
373 bp->p = NULL;
374 bp->s = 0;
375 bp->i = 0; /* start from the beginning again */
376 return;
377 }
378
379 if(!bp->p) {
380 bp->s = bp->as[bp->i];
381 bp->p = (unsigned char *) ALLOC(a, bp->s);
382 if(!bp->p)
383 fail(t_no, "ALLOC(%lu) failed [id=%d])\n", bp->s, id);
384 memset((void *) bp->p, (unsigned char)id, (size_t) bp->s);
385 }
386 else {
387 unsigned char *p = (unsigned char *) REALLOC(a, bp->p, bp->as[bp->i]);
388 if(!p)
389 fail(t_no, "REALLOC(0x%lx, %lu) failed [id=%d]\n",
390 (Ulong) bp->p, bp->as[bp->i], id);
391
392 if(bp->s < bp->as[bp->i]) {
393 CHECK_BLOCK_DATA(t_no, p, bp->s, id);
394 memset((void *) p, (unsigned char)id, (size_t) bp->as[bp->i]);
395 }
396 else
397 CHECK_BLOCK_DATA(t_no, p, bp->as[bp->i], id);
398
399 bp->s = bp->as[bp->i];
400 bp->p = p;
401 }
402
403 bp->i++;
404 }
405
406
407 void *
thread_func(void * arg)408 thread_func(void *arg)
409 {
410 int i, j;
411 ThreadData *td = ((ThreadData *) arg);
412 block bs[NO_OF_BLOCKS];
413
414 for(i = 0; i < NO_OF_BLOCKS; i++) {
415 bs[i].p = NULL;
416 bs[i].s = 0;
417 bs[i].i = 0;
418 bs[i].as = alloc_seqs[i % NO_OF_ALLOC_SEQS];
419 }
420
421 for(i = 0; i < td->no_ops_per_bl; i++) {
422
423 for(j = 0; j < NO_OF_BLOCKS; j++)
424 alloc_op(td->t_no, td->a, &bs[j], BLOCK_ID(td->t_no, j), 0);
425 }
426
427 for(j = 0; j < NO_OF_BLOCKS; j++)
428 alloc_op(td->t_no, td->a, &bs[j], BLOCK_ID(td->t_no, j), 1);
429
430 exit_thread(td->t_no, 1);
431 return NULL;
432 }
433
434 ERL_NIF_INIT(threads, testcase_nif_funcs, testcase_nif_init,
435 NULL, NULL, NULL);
436