]>
Commit | Line | Data |
---|---|---|
f54d4287 BM |
1 | /* |
2 | * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers | |
3 | * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. | |
4 | * Copyright (c) 1996 by Silicon Graphics. All rights reserved. | |
5 | * | |
6 | * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED | |
7 | * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. | |
8 | * | |
9 | * Permission is hereby granted to use or copy this program | |
10 | * for any purpose, provided the above notices are retained on all copies. | |
11 | * Permission to modify the code and to distribute modified code is granted, | |
12 | * provided the above notices are retained, and a notice that the code was | |
13 | * modified is included with the above copyright notice. | |
14 | */ | |
15 | /* An incomplete test for the garbage collector. */ | |
16 | /* Some more obscure entry points are not tested at all. */ | |
5a2586cf TT |
17 | /* This must be compiled with the same flags used to build the */ |
18 | /* GC. It uses GC internals to allow more precise results */ | |
19 | /* checking for some of the tests. */ | |
f54d4287 BM |
20 | |
21 | # undef GC_BUILD | |
22 | ||
79f777fd | 23 | #if defined(DBG_HDRS_ALL) || defined(MAKE_BACK_GRAPH) |
f54d4287 BM |
24 | # define GC_DEBUG |
25 | #endif | |
26 | ||
27 | # if defined(mips) && defined(SYSTYPE_BSD43) | |
28 | /* MIPS RISCOS 4 */ | |
29 | # else | |
30 | # include <stdlib.h> | |
31 | # endif | |
32 | # include <stdio.h> | |
33 | # ifdef _WIN32_WCE | |
34 | # include <winbase.h> | |
35 | # define assert ASSERT | |
36 | # else | |
37 | # include <assert.h> /* Not normally used, but handy for debugging. */ | |
38 | # endif | |
39 | # include <assert.h> /* Not normally used, but handy for debugging. */ | |
40 | # include "gc.h" | |
41 | # include "gc_typed.h" | |
42 | # ifdef THREAD_LOCAL_ALLOC | |
43 | # include "gc_local_alloc.h" | |
44 | # endif | |
45 | # include "private/gc_priv.h" /* For output, locking, MIN_WORDS, */ | |
30c3de1f | 46 | /* and some statistics. */ |
f54d4287 BM |
47 | # include "private/gcconfig.h" |
48 | ||
49 | # if defined(MSWIN32) || defined(MSWINCE) | |
50 | # include <windows.h> | |
51 | # endif | |
52 | ||
53 | # ifdef PCR | |
54 | # include "th/PCR_ThCrSec.h" | |
55 | # include "th/PCR_Th.h" | |
56 | # undef GC_printf0 | |
57 | # define GC_printf0 printf | |
58 | # undef GC_printf1 | |
59 | # define GC_printf1 printf | |
60 | # endif | |
61 | ||
4c7726b1 | 62 | # if defined(GC_SOLARIS_THREADS) && !defined(GC_SOLARIS_PTHREADS) |
f54d4287 BM |
63 | # include <thread.h> |
64 | # include <synch.h> | |
65 | # endif | |
66 | ||
4c7726b1 | 67 | # if defined(GC_PTHREADS) |
f54d4287 BM |
68 | # include <pthread.h> |
69 | # endif | |
70 | ||
30c3de1f | 71 | # if defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS) |
f54d4287 BM |
72 | static CRITICAL_SECTION incr_cs; |
73 | # endif | |
74 | ||
30c3de1f JS |
75 | #ifdef __STDC__ |
76 | # include <stdarg.h> | |
77 | #endif | |
78 | ||
f54d4287 BM |
79 | |
80 | /* Allocation Statistics */ | |
81 | int stubborn_count = 0; | |
82 | int uncollectable_count = 0; | |
83 | int collectable_count = 0; | |
84 | int atomic_count = 0; | |
85 | int realloc_count = 0; | |
86 | ||
87 | #if defined(GC_AMIGA_FASTALLOC) && defined(AMIGA) | |
88 | ||
89 | extern void GC_amiga_free_all_mem(void); | |
90 | void Amiga_Fail(void){GC_amiga_free_all_mem();abort();} | |
91 | # define FAIL (void)Amiga_Fail() | |
92 | void *GC_amiga_gctest_malloc_explicitly_typed(size_t lb, GC_descr d){ | |
93 | void *ret=GC_malloc_explicitly_typed(lb,d); | |
94 | if(ret==NULL){ | |
95 | if(!GC_dont_gc){ | |
96 | GC_gcollect(); | |
97 | ret=GC_malloc_explicitly_typed(lb,d); | |
98 | } | |
99 | if(ret==NULL){ | |
100 | GC_printf0("Out of memory, (typed allocations are not directly " | |
101 | "supported with the GC_AMIGA_FASTALLOC option.)\n"); | |
102 | FAIL; | |
103 | } | |
104 | } | |
105 | return ret; | |
106 | } | |
107 | void *GC_amiga_gctest_calloc_explicitly_typed(size_t a,size_t lb, GC_descr d){ | |
108 | void *ret=GC_calloc_explicitly_typed(a,lb,d); | |
109 | if(ret==NULL){ | |
110 | if(!GC_dont_gc){ | |
111 | GC_gcollect(); | |
112 | ret=GC_calloc_explicitly_typed(a,lb,d); | |
113 | } | |
114 | if(ret==NULL){ | |
115 | GC_printf0("Out of memory, (typed allocations are not directly " | |
116 | "supported with the GC_AMIGA_FASTALLOC option.)\n"); | |
117 | FAIL; | |
118 | } | |
119 | } | |
120 | return ret; | |
121 | } | |
122 | # define GC_malloc_explicitly_typed(a,b) GC_amiga_gctest_malloc_explicitly_typed(a,b) | |
123 | # define GC_calloc_explicitly_typed(a,b,c) GC_amiga_gctest_calloc_explicitly_typed(a,b,c) | |
124 | ||
125 | #else /* !AMIGA_FASTALLOC */ | |
126 | ||
127 | # ifdef PCR | |
128 | # define FAIL (void)abort() | |
129 | # else | |
130 | # ifdef MSWINCE | |
131 | # define FAIL DebugBreak() | |
132 | # else | |
133 | # define FAIL GC_abort("Test failed"); | |
134 | # endif | |
135 | # endif | |
136 | ||
137 | #endif /* !AMIGA_FASTALLOC */ | |
138 | ||
139 | /* AT_END may be defined to exercise the interior pointer test */ | |
140 | /* if the collector is configured with ALL_INTERIOR_POINTERS. */ | |
141 | /* As it stands, this test should succeed with either */ | |
142 | /* configuration. In the FIND_LEAK configuration, it should */ | |
143 | /* find lots of leaks, since we free almost nothing. */ | |
144 | ||
145 | struct SEXPR { | |
146 | struct SEXPR * sexpr_car; | |
147 | struct SEXPR * sexpr_cdr; | |
148 | }; | |
149 | ||
150 | ||
151 | typedef struct SEXPR * sexpr; | |
152 | ||
153 | # define INT_TO_SEXPR(x) ((sexpr)(unsigned long)(x)) | |
154 | ||
155 | # undef nil | |
156 | # define nil (INT_TO_SEXPR(0)) | |
157 | # define car(x) ((x) -> sexpr_car) | |
158 | # define cdr(x) ((x) -> sexpr_cdr) | |
159 | # define is_nil(x) ((x) == nil) | |
160 | ||
161 | ||
162 | int extra_count = 0; /* Amount of space wasted in cons node */ | |
163 | ||
164 | /* Silly implementation of Lisp cons. Intentionally wastes lots of space */ | |
165 | /* to test collector. */ | |
166 | # ifdef VERY_SMALL_CONFIG | |
167 | # define cons small_cons | |
168 | # else | |
169 | sexpr cons (x, y) | |
170 | sexpr x; | |
171 | sexpr y; | |
172 | { | |
173 | register sexpr r; | |
174 | register int *p; | |
175 | register int my_extra = extra_count; | |
176 | ||
177 | stubborn_count++; | |
178 | r = (sexpr) GC_MALLOC_STUBBORN(sizeof(struct SEXPR) + my_extra); | |
179 | if (r == 0) { | |
180 | (void)GC_printf0("Out of memory\n"); | |
181 | exit(1); | |
182 | } | |
183 | for (p = (int *)r; | |
184 | ((char *)p) < ((char *)r) + my_extra + sizeof(struct SEXPR); p++) { | |
185 | if (*p) { | |
186 | (void)GC_printf1("Found nonzero at 0x%lx - allocator is broken\n", | |
187 | (unsigned long)p); | |
188 | FAIL; | |
189 | } | |
190 | *p = 13; | |
191 | } | |
192 | # ifdef AT_END | |
193 | r = (sexpr)((char *)r + (my_extra & ~7)); | |
194 | # endif | |
195 | r -> sexpr_car = x; | |
196 | r -> sexpr_cdr = y; | |
197 | my_extra++; | |
198 | if ( my_extra >= 5000 ) { | |
199 | extra_count = 0; | |
200 | } else { | |
201 | extra_count = my_extra; | |
202 | } | |
203 | GC_END_STUBBORN_CHANGE((char *)r); | |
204 | return(r); | |
205 | } | |
206 | # endif | |
207 | ||
f54d4287 BM |
208 | #ifdef GC_GCJ_SUPPORT |
209 | ||
5a2586cf | 210 | #include "gc_mark.h" |
8948cb39 | 211 | #include "gc_gcj.h" |
f54d4287 BM |
212 | |
213 | /* The following struct emulates the vtable in gcj. */ | |
214 | /* This assumes the default value of MARK_DESCR_OFFSET. */ | |
215 | struct fake_vtable { | |
216 | void * dummy; /* class pointer in real gcj. */ | |
217 | size_t descr; | |
218 | }; | |
219 | ||
220 | struct fake_vtable gcj_class_struct1 = { 0, sizeof(struct SEXPR) | |
221 | + sizeof(struct fake_vtable *) }; | |
222 | /* length based descriptor. */ | |
223 | struct fake_vtable gcj_class_struct2 = | |
8948cb39 | 224 | { 0, (3l << (CPP_WORDSZ - 3)) | GC_DS_BITMAP}; |
f54d4287 BM |
225 | /* Bitmap based descriptor. */ |
226 | ||
227 | struct GC_ms_entry * fake_gcj_mark_proc(word * addr, | |
8948cb39 BM |
228 | struct GC_ms_entry *mark_stack_ptr, |
229 | struct GC_ms_entry *mark_stack_limit, | |
f54d4287 BM |
230 | word env ) |
231 | { | |
232 | sexpr x; | |
233 | if (1 == env) { | |
234 | /* Object allocated with debug allocator. */ | |
4109fe85 | 235 | addr = (word *)GC_USR_PTR_FROM_BASE(addr); |
f54d4287 BM |
236 | } |
237 | x = (sexpr)(addr + 1); /* Skip the vtable pointer. */ | |
5a2586cf TT |
238 | mark_stack_ptr = GC_MARK_AND_PUSH( |
239 | (GC_PTR)(x -> sexpr_cdr), mark_stack_ptr, | |
240 | mark_stack_limit, (GC_PTR *)&(x -> sexpr_cdr)); | |
241 | mark_stack_ptr = GC_MARK_AND_PUSH( | |
242 | (GC_PTR)(x -> sexpr_car), mark_stack_ptr, | |
243 | mark_stack_limit, (GC_PTR *)&(x -> sexpr_car)); | |
f54d4287 BM |
244 | return(mark_stack_ptr); |
245 | } | |
246 | ||
30c3de1f JS |
247 | #endif /* GC_GCJ_SUPPORT */ |
248 | ||
249 | #ifdef THREAD_LOCAL_ALLOC | |
250 | ||
251 | #undef GC_REDIRECT_TO_LOCAL | |
252 | #include "gc_local_alloc.h" | |
253 | ||
254 | sexpr local_cons (x, y) | |
255 | sexpr x; | |
256 | sexpr y; | |
257 | { | |
258 | register sexpr r; | |
259 | register int *p; | |
260 | register int my_extra = extra_count; | |
261 | static int my_random = 0; | |
262 | ||
263 | collectable_count++; | |
264 | r = (sexpr) GC_LOCAL_MALLOC(sizeof(struct SEXPR) + my_extra); | |
265 | # ifdef GC_GCJ_SUPPORT | |
266 | if (collectable_count % 2 == 0) { | |
267 | r = (sexpr) GC_LOCAL_GCJ_MALLOC(sizeof(struct SEXPR) + sizeof(GC_word) + my_extra, | |
268 | &gcj_class_struct1); | |
269 | r = (sexpr) ((GC_word *)r + 1); | |
270 | } | |
271 | # endif | |
272 | if (r == 0) { | |
273 | (void)GC_printf0("Out of memory\n"); | |
274 | exit(1); | |
275 | } | |
276 | for (p = (int *)r; | |
277 | ((char *)p) < ((char *)r) + my_extra + sizeof(struct SEXPR); p++) { | |
278 | if (*p) { | |
279 | (void)GC_printf1("Found nonzero at 0x%lx (local) - allocator is broken\n", | |
280 | (unsigned long)p); | |
281 | FAIL; | |
282 | } | |
283 | *p = 13; | |
284 | } | |
285 | r -> sexpr_car = x; | |
286 | r -> sexpr_cdr = y; | |
287 | my_extra++; | |
288 | if ( my_extra >= 5000 || my_extra == 200 && ++my_random % 37 != 0) { | |
289 | extra_count = 0; | |
290 | } else { | |
291 | extra_count = my_extra; | |
292 | } | |
293 | return(r); | |
294 | } | |
295 | #endif /* THREAD_LOCAL_ALLOC */ | |
296 | ||
297 | sexpr small_cons (x, y) | |
298 | sexpr x; | |
299 | sexpr y; | |
300 | { | |
301 | register sexpr r; | |
302 | ||
303 | collectable_count++; | |
304 | r = (sexpr) GC_MALLOC(sizeof(struct SEXPR)); | |
305 | if (r == 0) { | |
306 | (void)GC_printf0("Out of memory\n"); | |
307 | exit(1); | |
308 | } | |
309 | r -> sexpr_car = x; | |
310 | r -> sexpr_cdr = y; | |
311 | return(r); | |
312 | } | |
313 | ||
314 | sexpr small_cons_uncollectable (x, y) | |
315 | sexpr x; | |
316 | sexpr y; | |
317 | { | |
318 | register sexpr r; | |
319 | ||
320 | uncollectable_count++; | |
321 | r = (sexpr) GC_MALLOC_UNCOLLECTABLE(sizeof(struct SEXPR)); | |
322 | if (r == 0) { | |
323 | (void)GC_printf0("Out of memory\n"); | |
324 | exit(1); | |
325 | } | |
326 | r -> sexpr_car = x; | |
327 | r -> sexpr_cdr = (sexpr)(~(unsigned long)y); | |
328 | return(r); | |
329 | } | |
330 | ||
331 | #ifdef GC_GCJ_SUPPORT | |
332 | ||
333 | ||
f54d4287 BM |
334 | sexpr gcj_cons(x, y) |
335 | sexpr x; | |
336 | sexpr y; | |
337 | { | |
338 | GC_word * r; | |
339 | sexpr result; | |
340 | static int count = 0; | |
341 | ||
342 | if (++count & 1) { | |
343 | # ifdef USE_MARK_BYTES | |
344 | r = (GC_word *) GC_GCJ_FAST_MALLOC(4, &gcj_class_struct1); | |
345 | # else | |
346 | r = (GC_word *) GC_GCJ_FAST_MALLOC(3, &gcj_class_struct1); | |
347 | # endif | |
348 | } else { | |
349 | r = (GC_word *) GC_GCJ_MALLOC(sizeof(struct SEXPR) | |
350 | + sizeof(struct fake_vtable*), | |
351 | &gcj_class_struct2); | |
352 | } | |
353 | if (r == 0) { | |
354 | (void)GC_printf0("Out of memory\n"); | |
355 | exit(1); | |
356 | } | |
357 | result = (sexpr)(r + 1); | |
358 | result -> sexpr_car = x; | |
359 | result -> sexpr_cdr = y; | |
360 | return(result); | |
361 | } | |
362 | #endif | |
363 | ||
364 | /* Return reverse(x) concatenated with y */ | |
365 | sexpr reverse1(x, y) | |
366 | sexpr x, y; | |
367 | { | |
368 | if (is_nil(x)) { | |
369 | return(y); | |
370 | } else { | |
371 | return( reverse1(cdr(x), cons(car(x), y)) ); | |
372 | } | |
373 | } | |
374 | ||
375 | sexpr reverse(x) | |
376 | sexpr x; | |
377 | { | |
30c3de1f JS |
378 | # ifdef TEST_WITH_SYSTEM_MALLOC |
379 | malloc(100000); | |
380 | # endif | |
f54d4287 BM |
381 | return( reverse1(x, nil) ); |
382 | } | |
383 | ||
384 | sexpr ints(low, up) | |
385 | int low, up; | |
386 | { | |
387 | if (low > up) { | |
388 | return(nil); | |
389 | } else { | |
390 | return(small_cons(small_cons(INT_TO_SEXPR(low), nil), ints(low+1, up))); | |
391 | } | |
392 | } | |
393 | ||
394 | #ifdef GC_GCJ_SUPPORT | |
395 | /* Return reverse(x) concatenated with y */ | |
396 | sexpr gcj_reverse1(x, y) | |
397 | sexpr x, y; | |
398 | { | |
399 | if (is_nil(x)) { | |
400 | return(y); | |
401 | } else { | |
402 | return( gcj_reverse1(cdr(x), gcj_cons(car(x), y)) ); | |
403 | } | |
404 | } | |
405 | ||
406 | sexpr gcj_reverse(x) | |
407 | sexpr x; | |
408 | { | |
409 | return( gcj_reverse1(x, nil) ); | |
410 | } | |
411 | ||
412 | sexpr gcj_ints(low, up) | |
413 | int low, up; | |
414 | { | |
415 | if (low > up) { | |
416 | return(nil); | |
417 | } else { | |
418 | return(gcj_cons(gcj_cons(INT_TO_SEXPR(low), nil), gcj_ints(low+1, up))); | |
419 | } | |
420 | } | |
421 | #endif /* GC_GCJ_SUPPORT */ | |
422 | ||
30c3de1f JS |
423 | #ifdef THREAD_LOCAL_ALLOC |
424 | /* Return reverse(x) concatenated with y */ | |
425 | sexpr local_reverse1(x, y) | |
426 | sexpr x, y; | |
427 | { | |
428 | if (is_nil(x)) { | |
429 | return(y); | |
430 | } else { | |
431 | return( local_reverse1(cdr(x), local_cons(car(x), y)) ); | |
432 | } | |
433 | } | |
434 | ||
435 | sexpr local_reverse(x) | |
436 | sexpr x; | |
437 | { | |
438 | return( local_reverse1(x, nil) ); | |
439 | } | |
440 | ||
441 | sexpr local_ints(low, up) | |
442 | int low, up; | |
443 | { | |
444 | if (low > up) { | |
445 | return(nil); | |
446 | } else { | |
447 | return(local_cons(local_cons(INT_TO_SEXPR(low), nil), local_ints(low+1, up))); | |
448 | } | |
449 | } | |
450 | #endif /* THREAD_LOCAL_ALLOC */ | |
451 | ||
f54d4287 BM |
452 | /* To check uncollectable allocation we build lists with disguised cdr */ |
453 | /* pointers, and make sure they don't go away. */ | |
454 | sexpr uncollectable_ints(low, up) | |
455 | int low, up; | |
456 | { | |
457 | if (low > up) { | |
458 | return(nil); | |
459 | } else { | |
460 | return(small_cons_uncollectable(small_cons(INT_TO_SEXPR(low), nil), | |
461 | uncollectable_ints(low+1, up))); | |
462 | } | |
463 | } | |
464 | ||
465 | void check_ints(list, low, up) | |
466 | sexpr list; | |
467 | int low, up; | |
468 | { | |
469 | if ((int)(GC_word)(car(car(list))) != low) { | |
470 | (void)GC_printf0( | |
471 | "List reversal produced incorrect list - collector is broken\n"); | |
472 | FAIL; | |
473 | } | |
474 | if (low == up) { | |
475 | if (cdr(list) != nil) { | |
476 | (void)GC_printf0("List too long - collector is broken\n"); | |
477 | FAIL; | |
478 | } | |
479 | } else { | |
480 | check_ints(cdr(list), low+1, up); | |
481 | } | |
482 | } | |
483 | ||
484 | # define UNCOLLECTABLE_CDR(x) (sexpr)(~(unsigned long)(cdr(x))) | |
485 | ||
486 | void check_uncollectable_ints(list, low, up) | |
487 | sexpr list; | |
488 | int low, up; | |
489 | { | |
490 | if ((int)(GC_word)(car(car(list))) != low) { | |
491 | (void)GC_printf0( | |
492 | "Uncollectable list corrupted - collector is broken\n"); | |
493 | FAIL; | |
494 | } | |
495 | if (low == up) { | |
496 | if (UNCOLLECTABLE_CDR(list) != nil) { | |
497 | (void)GC_printf0("Uncollectable list too long - collector is broken\n"); | |
498 | FAIL; | |
499 | } | |
500 | } else { | |
501 | check_uncollectable_ints(UNCOLLECTABLE_CDR(list), low+1, up); | |
502 | } | |
503 | } | |
504 | ||
505 | /* Not used, but useful for debugging: */ | |
506 | void print_int_list(x) | |
507 | sexpr x; | |
508 | { | |
509 | if (is_nil(x)) { | |
510 | (void)GC_printf0("NIL\n"); | |
511 | } else { | |
512 | (void)GC_printf1("(%ld)", (long)(car(car(x)))); | |
513 | if (!is_nil(cdr(x))) { | |
514 | (void)GC_printf0(", "); | |
515 | (void)print_int_list(cdr(x)); | |
516 | } else { | |
517 | (void)GC_printf0("\n"); | |
518 | } | |
519 | } | |
520 | } | |
521 | ||
f54d4287 BM |
522 | /* |
523 | * A tiny list reversal test to check thread creation. | |
524 | */ | |
525 | #ifdef THREADS | |
526 | ||
30c3de1f JS |
527 | # if defined(GC_WIN32_THREADS) && !defined(CYGWIN32) |
528 | DWORD __stdcall tiny_reverse_test(void * arg) | |
f54d4287 BM |
529 | # else |
530 | void * tiny_reverse_test(void * arg) | |
531 | # endif | |
532 | { | |
30c3de1f JS |
533 | int i; |
534 | for (i = 0; i < 5; ++i) { | |
535 | check_ints(reverse(reverse(ints(1,10))), 1, 10); | |
536 | # ifdef THREAD_LOCAL_ALLOC | |
537 | check_ints(local_reverse(local_reverse(local_ints(1,10))), 1, 10); | |
538 | # endif | |
539 | } | |
f54d4287 BM |
540 | return 0; |
541 | } | |
542 | ||
4c7726b1 | 543 | # if defined(GC_PTHREADS) |
f54d4287 BM |
544 | void fork_a_thread() |
545 | { | |
546 | pthread_t t; | |
547 | int code; | |
548 | if ((code = pthread_create(&t, 0, tiny_reverse_test, 0)) != 0) { | |
549 | (void)GC_printf1("Small thread creation failed %lu\n", | |
550 | (unsigned long)code); | |
551 | FAIL; | |
552 | } | |
553 | if ((code = pthread_join(t, 0)) != 0) { | |
554 | (void)GC_printf1("Small thread join failed %lu\n", | |
555 | (unsigned long)code); | |
556 | FAIL; | |
557 | } | |
558 | } | |
559 | ||
4c7726b1 | 560 | # elif defined(GC_WIN32_THREADS) |
f54d4287 BM |
561 | void fork_a_thread() |
562 | { | |
30c3de1f | 563 | DWORD thread_id; |
f54d4287 BM |
564 | HANDLE h; |
565 | h = GC_CreateThread(NULL, 0, tiny_reverse_test, 0, 0, &thread_id); | |
566 | if (h == (HANDLE)NULL) { | |
567 | (void)GC_printf1("Small thread creation failed %lu\n", | |
568 | (unsigned long)GetLastError()); | |
569 | FAIL; | |
570 | } | |
571 | if (WaitForSingleObject(h, INFINITE) != WAIT_OBJECT_0) { | |
572 | (void)GC_printf1("Small thread wait failed %lu\n", | |
573 | (unsigned long)GetLastError()); | |
574 | FAIL; | |
575 | } | |
576 | } | |
577 | ||
4c7726b1 | 578 | /* # elif defined(GC_SOLARIS_THREADS) */ |
f54d4287 BM |
579 | |
580 | # else | |
581 | ||
582 | # define fork_a_thread() | |
583 | ||
584 | # endif | |
585 | ||
586 | #else | |
587 | ||
588 | # define fork_a_thread() | |
589 | ||
590 | #endif | |
591 | ||
30c3de1f JS |
592 | /* Try to force a to be strangely aligned */ |
593 | struct { | |
594 | char dummy; | |
595 | sexpr aa; | |
596 | } A; | |
597 | #define a A.aa | |
598 | ||
f54d4287 BM |
599 | /* |
600 | * Repeatedly reverse lists built out of very different sized cons cells. | |
601 | * Check that we didn't lose anything. | |
602 | */ | |
603 | void reverse_test() | |
604 | { | |
605 | int i; | |
606 | sexpr b; | |
607 | sexpr c; | |
608 | sexpr d; | |
609 | sexpr e; | |
610 | sexpr *f, *g, *h; | |
611 | # if defined(MSWIN32) || defined(MACOS) | |
612 | /* Win32S only allows 128K stacks */ | |
613 | # define BIG 1000 | |
614 | # else | |
615 | # if defined PCR | |
616 | /* PCR default stack is 100K. Stack frames are up to 120 bytes. */ | |
617 | # define BIG 700 | |
618 | # else | |
619 | # if defined MSWINCE | |
620 | /* WinCE only allows 64K stacks */ | |
621 | # define BIG 500 | |
622 | # else | |
623 | # if defined(OSF1) | |
624 | /* OSF has limited stack space by default, and large frames. */ | |
625 | # define BIG 200 | |
626 | # else | |
627 | # define BIG 4500 | |
628 | # endif | |
629 | # endif | |
630 | # endif | |
631 | # endif | |
632 | ||
633 | A.dummy = 17; | |
634 | a = ints(1, 49); | |
635 | b = ints(1, 50); | |
636 | c = ints(1, BIG); | |
637 | d = uncollectable_ints(1, 100); | |
638 | e = uncollectable_ints(1, 1); | |
639 | /* Check that realloc updates object descriptors correctly */ | |
640 | collectable_count++; | |
641 | f = (sexpr *)GC_MALLOC(4 * sizeof(sexpr)); | |
642 | realloc_count++; | |
643 | f = (sexpr *)GC_REALLOC((GC_PTR)f, 6 * sizeof(sexpr)); | |
644 | f[5] = ints(1,17); | |
645 | collectable_count++; | |
646 | g = (sexpr *)GC_MALLOC(513 * sizeof(sexpr)); | |
647 | realloc_count++; | |
648 | g = (sexpr *)GC_REALLOC((GC_PTR)g, 800 * sizeof(sexpr)); | |
649 | g[799] = ints(1,18); | |
650 | collectable_count++; | |
651 | h = (sexpr *)GC_MALLOC(1025 * sizeof(sexpr)); | |
652 | realloc_count++; | |
653 | h = (sexpr *)GC_REALLOC((GC_PTR)h, 2000 * sizeof(sexpr)); | |
654 | # ifdef GC_GCJ_SUPPORT | |
655 | h[1999] = gcj_ints(1,200); | |
30c3de1f JS |
656 | for (i = 0; i < 51; ++i) |
657 | h[1999] = gcj_reverse(h[1999]); | |
658 | /* Leave it as the reveresed list for now. */ | |
f54d4287 BM |
659 | # else |
660 | h[1999] = ints(1,200); | |
661 | # endif | |
662 | /* Try to force some collections and reuse of small list elements */ | |
663 | for (i = 0; i < 10; i++) { | |
664 | (void)ints(1, BIG); | |
665 | } | |
666 | /* Superficially test interior pointer recognition on stack */ | |
667 | c = (sexpr)((char *)c + sizeof(char *)); | |
668 | d = (sexpr)((char *)d + sizeof(char *)); | |
669 | ||
670 | # ifdef __STDC__ | |
671 | GC_FREE((void *)e); | |
672 | # else | |
673 | GC_FREE((char *)e); | |
674 | # endif | |
675 | check_ints(b,1,50); | |
676 | check_ints(a,1,49); | |
677 | for (i = 0; i < 50; i++) { | |
678 | check_ints(b,1,50); | |
679 | b = reverse(reverse(b)); | |
680 | } | |
681 | check_ints(b,1,50); | |
682 | check_ints(a,1,49); | |
683 | for (i = 0; i < 60; i++) { | |
684 | if (i % 10 == 0) fork_a_thread(); | |
685 | /* This maintains the invariant that a always points to a list of */ | |
686 | /* 49 integers. Thus this is thread safe without locks, */ | |
687 | /* assuming atomic pointer assignments. */ | |
688 | a = reverse(reverse(a)); | |
30c3de1f JS |
689 | # ifdef THREAD_LOCAL_ALLOC |
690 | a = local_reverse(local_reverse(a)); | |
691 | # endif | |
f54d4287 BM |
692 | # if !defined(AT_END) && !defined(THREADS) |
693 | /* This is not thread safe, since realloc explicitly deallocates */ | |
694 | if (i & 1) { | |
695 | a = (sexpr)GC_REALLOC((GC_PTR)a, 500); | |
696 | } else { | |
697 | a = (sexpr)GC_REALLOC((GC_PTR)a, 8200); | |
698 | } | |
699 | # endif | |
700 | } | |
701 | check_ints(a,1,49); | |
702 | check_ints(b,1,50); | |
703 | c = (sexpr)((char *)c - sizeof(char *)); | |
704 | d = (sexpr)((char *)d - sizeof(char *)); | |
705 | check_ints(c,1,BIG); | |
706 | check_uncollectable_ints(d, 1, 100); | |
707 | check_ints(f[5], 1,17); | |
708 | check_ints(g[799], 1,18); | |
709 | # ifdef GC_GCJ_SUPPORT | |
710 | h[1999] = gcj_reverse(h[1999]); | |
711 | # endif | |
712 | check_ints(h[1999], 1,200); | |
713 | # ifndef THREADS | |
714 | a = 0; | |
715 | # endif | |
716 | b = c = 0; | |
717 | } | |
718 | ||
30c3de1f JS |
719 | #undef a |
720 | ||
f54d4287 BM |
721 | /* |
722 | * The rest of this builds balanced binary trees, checks that they don't | |
723 | * disappear, and tests finalization. | |
724 | */ | |
725 | typedef struct treenode { | |
726 | int level; | |
727 | struct treenode * lchild; | |
728 | struct treenode * rchild; | |
729 | } tn; | |
730 | ||
731 | int finalizable_count = 0; | |
732 | int finalized_count = 0; | |
733 | VOLATILE int dropped_something = 0; | |
734 | ||
735 | # ifdef __STDC__ | |
736 | void finalizer(void * obj, void * client_data) | |
737 | # else | |
738 | void finalizer(obj, client_data) | |
739 | char * obj; | |
740 | char * client_data; | |
741 | # endif | |
742 | { | |
743 | tn * t = (tn *)obj; | |
744 | ||
745 | # ifdef PCR | |
746 | PCR_ThCrSec_EnterSys(); | |
747 | # endif | |
4c7726b1 | 748 | # if defined(GC_SOLARIS_THREADS) && !defined(GC_SOLARIS_PTHREADS) |
f54d4287 BM |
749 | static mutex_t incr_lock; |
750 | mutex_lock(&incr_lock); | |
751 | # endif | |
4c7726b1 | 752 | # if defined(GC_PTHREADS) |
f54d4287 BM |
753 | static pthread_mutex_t incr_lock = PTHREAD_MUTEX_INITIALIZER; |
754 | pthread_mutex_lock(&incr_lock); | |
30c3de1f JS |
755 | # else |
756 | # ifdef GC_WIN32_THREADS | |
757 | EnterCriticalSection(&incr_cs); | |
758 | # endif | |
f54d4287 BM |
759 | # endif |
760 | if ((int)(GC_word)client_data != t -> level) { | |
761 | (void)GC_printf0("Wrong finalization data - collector is broken\n"); | |
762 | FAIL; | |
763 | } | |
764 | finalized_count++; | |
30c3de1f | 765 | t -> level = -1; /* detect duplicate finalization immediately */ |
f54d4287 BM |
766 | # ifdef PCR |
767 | PCR_ThCrSec_ExitSys(); | |
768 | # endif | |
4c7726b1 | 769 | # if defined(GC_SOLARIS_THREADS) && !defined(GC_SOLARIS_PTHREADS) |
f54d4287 BM |
770 | mutex_unlock(&incr_lock); |
771 | # endif | |
4c7726b1 | 772 | # if defined(GC_PTHREADS) |
f54d4287 | 773 | pthread_mutex_unlock(&incr_lock); |
30c3de1f JS |
774 | # else |
775 | # ifdef GC_WIN32_THREADS | |
776 | LeaveCriticalSection(&incr_cs); | |
777 | # endif | |
f54d4287 BM |
778 | # endif |
779 | } | |
780 | ||
781 | size_t counter = 0; | |
782 | ||
783 | # define MAX_FINALIZED 8000 | |
784 | ||
785 | # if !defined(MACOS) | |
786 | GC_FAR GC_word live_indicators[MAX_FINALIZED] = {0}; | |
787 | #else | |
788 | /* Too big for THINK_C. have to allocate it dynamically. */ | |
789 | GC_word *live_indicators = 0; | |
790 | #endif | |
791 | ||
792 | int live_indicators_count = 0; | |
793 | ||
794 | tn * mktree(n) | |
795 | int n; | |
796 | { | |
797 | # ifdef THREAD_LOCAL_ALLOC | |
798 | tn * result = (tn *)GC_LOCAL_MALLOC(sizeof(tn)); | |
799 | # else | |
800 | tn * result = (tn *)GC_MALLOC(sizeof(tn)); | |
801 | # endif | |
802 | ||
803 | collectable_count++; | |
5a2586cf TT |
804 | # ifdef THREAD_LOCAL_ALLOC |
805 | /* Minimally exercise thread local allocation */ | |
806 | { | |
807 | char * result = (char *)GC_LOCAL_MALLOC_ATOMIC(17); | |
808 | memset(result, 'a', 17); | |
809 | } | |
810 | # endif /* THREAD_LOCAL_ALLOC */ | |
f54d4287 BM |
811 | # if defined(MACOS) |
812 | /* get around static data limitations. */ | |
813 | if (!live_indicators) | |
814 | live_indicators = | |
815 | (GC_word*)NewPtrClear(MAX_FINALIZED * sizeof(GC_word)); | |
816 | if (!live_indicators) { | |
817 | (void)GC_printf0("Out of memory\n"); | |
818 | exit(1); | |
819 | } | |
820 | # endif | |
821 | if (n == 0) return(0); | |
822 | if (result == 0) { | |
823 | (void)GC_printf0("Out of memory\n"); | |
824 | exit(1); | |
825 | } | |
826 | result -> level = n; | |
827 | result -> lchild = mktree(n-1); | |
828 | result -> rchild = mktree(n-1); | |
829 | if (counter++ % 17 == 0 && n >= 2) { | |
830 | tn * tmp = result -> lchild -> rchild; | |
831 | ||
832 | result -> lchild -> rchild = result -> rchild -> lchild; | |
833 | result -> rchild -> lchild = tmp; | |
834 | } | |
835 | if (counter++ % 119 == 0) { | |
836 | int my_index; | |
837 | ||
838 | { | |
839 | # ifdef PCR | |
840 | PCR_ThCrSec_EnterSys(); | |
841 | # endif | |
4c7726b1 | 842 | # if defined(GC_SOLARIS_THREADS) && !defined(GC_SOLARIS_PTHREADS) |
f54d4287 BM |
843 | static mutex_t incr_lock; |
844 | mutex_lock(&incr_lock); | |
845 | # endif | |
4c7726b1 | 846 | # if defined(GC_PTHREADS) |
f54d4287 BM |
847 | static pthread_mutex_t incr_lock = PTHREAD_MUTEX_INITIALIZER; |
848 | pthread_mutex_lock(&incr_lock); | |
30c3de1f JS |
849 | # else |
850 | # ifdef GC_WIN32_THREADS | |
851 | EnterCriticalSection(&incr_cs); | |
852 | # endif | |
f54d4287 BM |
853 | # endif |
854 | /* Losing a count here causes erroneous report of failure. */ | |
855 | finalizable_count++; | |
856 | my_index = live_indicators_count++; | |
857 | # ifdef PCR | |
858 | PCR_ThCrSec_ExitSys(); | |
859 | # endif | |
4c7726b1 | 860 | # if defined(GC_SOLARIS_THREADS) && !defined(GC_SOLARIS_PTHREADS) |
f54d4287 BM |
861 | mutex_unlock(&incr_lock); |
862 | # endif | |
4c7726b1 | 863 | # if defined(GC_PTHREADS) |
f54d4287 | 864 | pthread_mutex_unlock(&incr_lock); |
30c3de1f JS |
865 | # else |
866 | # ifdef GC_WIN32_THREADS | |
867 | LeaveCriticalSection(&incr_cs); | |
868 | # endif | |
f54d4287 BM |
869 | # endif |
870 | } | |
871 | ||
872 | GC_REGISTER_FINALIZER((GC_PTR)result, finalizer, (GC_PTR)(GC_word)n, | |
873 | (GC_finalization_proc *)0, (GC_PTR *)0); | |
874 | if (my_index >= MAX_FINALIZED) { | |
875 | GC_printf0("live_indicators overflowed\n"); | |
876 | FAIL; | |
877 | } | |
878 | live_indicators[my_index] = 13; | |
879 | if (GC_GENERAL_REGISTER_DISAPPEARING_LINK( | |
880 | (GC_PTR *)(&(live_indicators[my_index])), | |
881 | (GC_PTR)result) != 0) { | |
882 | GC_printf0("GC_general_register_disappearing_link failed\n"); | |
883 | FAIL; | |
884 | } | |
885 | if (GC_unregister_disappearing_link( | |
886 | (GC_PTR *) | |
887 | (&(live_indicators[my_index]))) == 0) { | |
888 | GC_printf0("GC_unregister_disappearing_link failed\n"); | |
889 | FAIL; | |
890 | } | |
891 | if (GC_GENERAL_REGISTER_DISAPPEARING_LINK( | |
892 | (GC_PTR *)(&(live_indicators[my_index])), | |
893 | (GC_PTR)result) != 0) { | |
894 | GC_printf0("GC_general_register_disappearing_link failed 2\n"); | |
895 | FAIL; | |
896 | } | |
897 | } | |
898 | return(result); | |
899 | } | |
900 | ||
901 | void chktree(t,n) | |
902 | tn *t; | |
903 | int n; | |
904 | { | |
905 | if (n == 0 && t != 0) { | |
906 | (void)GC_printf0("Clobbered a leaf - collector is broken\n"); | |
907 | FAIL; | |
908 | } | |
909 | if (n == 0) return; | |
910 | if (t -> level != n) { | |
911 | (void)GC_printf1("Lost a node at level %lu - collector is broken\n", | |
912 | (unsigned long)n); | |
913 | FAIL; | |
914 | } | |
915 | if (counter++ % 373 == 0) { | |
916 | collectable_count++; | |
917 | (void) GC_MALLOC(counter%5001); | |
918 | } | |
919 | chktree(t -> lchild, n-1); | |
920 | if (counter++ % 73 == 0) { | |
921 | collectable_count++; | |
922 | (void) GC_MALLOC(counter%373); | |
923 | } | |
924 | chktree(t -> rchild, n-1); | |
925 | } | |
926 | ||
4c7726b1 | 927 | # if defined(GC_SOLARIS_THREADS) && !defined(GC_SOLARIS_PTHREADS) |
f54d4287 BM |
928 | thread_key_t fl_key; |
929 | ||
930 | void * alloc8bytes() | |
931 | { | |
932 | # if defined(SMALL_CONFIG) || defined(GC_DEBUG) | |
933 | collectable_count++; | |
934 | return(GC_MALLOC(8)); | |
935 | # else | |
936 | void ** my_free_list_ptr; | |
937 | void * my_free_list; | |
938 | ||
939 | if (thr_getspecific(fl_key, (void **)(&my_free_list_ptr)) != 0) { | |
940 | (void)GC_printf0("thr_getspecific failed\n"); | |
941 | FAIL; | |
942 | } | |
943 | if (my_free_list_ptr == 0) { | |
944 | uncollectable_count++; | |
945 | my_free_list_ptr = GC_NEW_UNCOLLECTABLE(void *); | |
946 | if (thr_setspecific(fl_key, my_free_list_ptr) != 0) { | |
947 | (void)GC_printf0("thr_setspecific failed\n"); | |
948 | FAIL; | |
949 | } | |
950 | } | |
951 | my_free_list = *my_free_list_ptr; | |
952 | if (my_free_list == 0) { | |
953 | collectable_count++; | |
954 | my_free_list = GC_malloc_many(8); | |
955 | if (my_free_list == 0) { | |
956 | (void)GC_printf0("alloc8bytes out of memory\n"); | |
957 | FAIL; | |
958 | } | |
959 | } | |
960 | *my_free_list_ptr = GC_NEXT(my_free_list); | |
961 | GC_NEXT(my_free_list) = 0; | |
962 | return(my_free_list); | |
963 | # endif | |
964 | } | |
965 | ||
966 | #else | |
967 | ||
4c7726b1 | 968 | # if defined(GC_PTHREADS) |
f54d4287 BM |
969 | pthread_key_t fl_key; |
970 | ||
971 | void * alloc8bytes() | |
972 | { | |
973 | # if defined(SMALL_CONFIG) || defined(GC_DEBUG) | |
974 | collectable_count++; | |
975 | return(GC_MALLOC(8)); | |
976 | # else | |
977 | void ** my_free_list_ptr; | |
978 | void * my_free_list; | |
979 | ||
980 | my_free_list_ptr = (void **)pthread_getspecific(fl_key); | |
981 | if (my_free_list_ptr == 0) { | |
982 | uncollectable_count++; | |
983 | my_free_list_ptr = GC_NEW_UNCOLLECTABLE(void *); | |
984 | if (pthread_setspecific(fl_key, my_free_list_ptr) != 0) { | |
985 | (void)GC_printf0("pthread_setspecific failed\n"); | |
986 | FAIL; | |
987 | } | |
988 | } | |
989 | my_free_list = *my_free_list_ptr; | |
990 | if (my_free_list == 0) { | |
991 | my_free_list = GC_malloc_many(8); | |
992 | if (my_free_list == 0) { | |
993 | (void)GC_printf0("alloc8bytes out of memory\n"); | |
994 | FAIL; | |
995 | } | |
996 | } | |
997 | *my_free_list_ptr = GC_NEXT(my_free_list); | |
998 | GC_NEXT(my_free_list) = 0; | |
999 | collectable_count++; | |
1000 | return(my_free_list); | |
1001 | # endif | |
1002 | } | |
1003 | ||
1004 | # else | |
1005 | # define alloc8bytes() GC_MALLOC_ATOMIC(8) | |
1006 | # endif | |
1007 | #endif | |
1008 | ||
1009 | void alloc_small(n) | |
1010 | int n; | |
1011 | { | |
1012 | register int i; | |
1013 | ||
1014 | for (i = 0; i < n; i += 8) { | |
1015 | atomic_count++; | |
1016 | if (alloc8bytes() == 0) { | |
1017 | (void)GC_printf0("Out of memory\n"); | |
1018 | FAIL; | |
1019 | } | |
1020 | } | |
1021 | } | |
1022 | ||
1023 | # if defined(THREADS) && defined(GC_DEBUG) | |
1024 | # ifdef VERY_SMALL_CONFIG | |
1025 | # define TREE_HEIGHT 12 | |
1026 | # else | |
1027 | # define TREE_HEIGHT 15 | |
1028 | # endif | |
1029 | # else | |
1030 | # ifdef VERY_SMALL_CONFIG | |
1031 | # define TREE_HEIGHT 13 | |
1032 | # else | |
1033 | # define TREE_HEIGHT 16 | |
1034 | # endif | |
1035 | # endif | |
1036 | void tree_test() | |
1037 | { | |
1038 | tn * root; | |
1039 | register int i; | |
1040 | ||
1041 | root = mktree(TREE_HEIGHT); | |
1042 | # ifndef VERY_SMALL_CONFIG | |
1043 | alloc_small(5000000); | |
1044 | # endif | |
1045 | chktree(root, TREE_HEIGHT); | |
1046 | if (finalized_count && ! dropped_something) { | |
1047 | (void)GC_printf0("Premature finalization - collector is broken\n"); | |
1048 | FAIL; | |
1049 | } | |
1050 | dropped_something = 1; | |
1051 | GC_noop(root); /* Root needs to remain live until */ | |
1052 | /* dropped_something is set. */ | |
1053 | root = mktree(TREE_HEIGHT); | |
1054 | chktree(root, TREE_HEIGHT); | |
1055 | for (i = TREE_HEIGHT; i >= 0; i--) { | |
1056 | root = mktree(i); | |
1057 | chktree(root, i); | |
1058 | } | |
1059 | # ifndef VERY_SMALL_CONFIG | |
1060 | alloc_small(5000000); | |
1061 | # endif | |
1062 | } | |
1063 | ||
1064 | unsigned n_tests = 0; | |
1065 | ||
1066 | GC_word bm_huge[10] = { | |
1067 | 0xffffffff, | |
1068 | 0xffffffff, | |
1069 | 0xffffffff, | |
1070 | 0xffffffff, | |
1071 | 0xffffffff, | |
1072 | 0xffffffff, | |
1073 | 0xffffffff, | |
1074 | 0xffffffff, | |
1075 | 0xffffffff, | |
1076 | 0x00ffffff, | |
1077 | }; | |
1078 | ||
1079 | /* A very simple test of explicitly typed allocation */ | |
1080 | void typed_test() | |
1081 | { | |
1082 | GC_word * old, * new; | |
1083 | GC_word bm3 = 0x3; | |
1084 | GC_word bm2 = 0x2; | |
1085 | GC_word bm_large = 0xf7ff7fff; | |
1086 | GC_descr d1 = GC_make_descriptor(&bm3, 2); | |
1087 | GC_descr d2 = GC_make_descriptor(&bm2, 2); | |
1088 | # ifndef LINT | |
1089 | GC_descr dummy = GC_make_descriptor(&bm_large, 32); | |
1090 | # endif | |
1091 | GC_descr d3 = GC_make_descriptor(&bm_large, 32); | |
1092 | GC_descr d4 = GC_make_descriptor(bm_huge, 320); | |
1093 | GC_word * x = (GC_word *)GC_malloc_explicitly_typed(2000, d4); | |
1094 | register int i; | |
1095 | ||
1096 | collectable_count++; | |
1097 | old = 0; | |
1098 | for (i = 0; i < 4000; i++) { | |
1099 | collectable_count++; | |
1100 | new = (GC_word *) GC_malloc_explicitly_typed(4 * sizeof(GC_word), d1); | |
1101 | if (0 != new[0] || 0 != new[1]) { | |
1102 | GC_printf0("Bad initialization by GC_malloc_explicitly_typed\n"); | |
1103 | FAIL; | |
1104 | } | |
1105 | new[0] = 17; | |
1106 | new[1] = (GC_word)old; | |
1107 | old = new; | |
1108 | collectable_count++; | |
1109 | new = (GC_word *) GC_malloc_explicitly_typed(4 * sizeof(GC_word), d2); | |
1110 | new[0] = 17; | |
1111 | new[1] = (GC_word)old; | |
1112 | old = new; | |
1113 | collectable_count++; | |
1114 | new = (GC_word *) GC_malloc_explicitly_typed(33 * sizeof(GC_word), d3); | |
1115 | new[0] = 17; | |
1116 | new[1] = (GC_word)old; | |
1117 | old = new; | |
1118 | collectable_count++; | |
1119 | new = (GC_word *) GC_calloc_explicitly_typed(4, 2 * sizeof(GC_word), | |
1120 | d1); | |
1121 | new[0] = 17; | |
1122 | new[1] = (GC_word)old; | |
1123 | old = new; | |
1124 | collectable_count++; | |
1125 | if (i & 0xff) { | |
1126 | new = (GC_word *) GC_calloc_explicitly_typed(7, 3 * sizeof(GC_word), | |
1127 | d2); | |
1128 | } else { | |
1129 | new = (GC_word *) GC_calloc_explicitly_typed(1001, | |
1130 | 3 * sizeof(GC_word), | |
1131 | d2); | |
1132 | if (0 != new[0] || 0 != new[1]) { | |
1133 | GC_printf0("Bad initialization by GC_malloc_explicitly_typed\n"); | |
1134 | FAIL; | |
1135 | } | |
1136 | } | |
1137 | new[0] = 17; | |
1138 | new[1] = (GC_word)old; | |
1139 | old = new; | |
1140 | } | |
1141 | for (i = 0; i < 20000; i++) { | |
1142 | if (new[0] != 17) { | |
1143 | (void)GC_printf1("typed alloc failed at %lu\n", | |
1144 | (unsigned long)i); | |
1145 | FAIL; | |
1146 | } | |
1147 | new[0] = 0; | |
1148 | old = new; | |
1149 | new = (GC_word *)(old[1]); | |
1150 | } | |
1151 | GC_gcollect(); | |
1152 | GC_noop(x); | |
1153 | } | |
1154 | ||
1155 | int fail_count = 0; | |
1156 | ||
1157 | #ifndef __STDC__ | |
1158 | /*ARGSUSED*/ | |
1159 | void fail_proc1(x) | |
1160 | GC_PTR x; | |
1161 | { | |
1162 | fail_count++; | |
1163 | } | |
1164 | ||
1165 | #else | |
1166 | ||
1167 | /*ARGSUSED*/ | |
1168 | void fail_proc1(GC_PTR x) | |
1169 | { | |
1170 | fail_count++; | |
1171 | } | |
1172 | ||
30c3de1f JS |
1173 | static void uniq(void *p, ...) { |
1174 | va_list a; | |
1175 | void *q[100]; | |
1176 | int n = 0, i, j; | |
1177 | q[n++] = p; | |
1178 | va_start(a,p); | |
1179 | for (;(q[n] = va_arg(a,void *));n++) ; | |
1180 | va_end(a); | |
1181 | for (i=0; i<n; i++) | |
1182 | for (j=0; j<i; j++) | |
1183 | if (q[i] == q[j]) { | |
1184 | GC_printf0( | |
1185 | "Apparently failed to mark form some function arguments.\n" | |
1186 | "Perhaps GC_push_regs was configured incorrectly?\n" | |
1187 | ); | |
1188 | FAIL; | |
1189 | } | |
1190 | } | |
1191 | ||
f54d4287 BM |
1192 | #endif /* __STDC__ */ |
1193 | ||
1194 | #ifdef THREADS | |
1195 | # define TEST_FAIL_COUNT(n) 1 | |
1196 | #else | |
1197 | # define TEST_FAIL_COUNT(n) (fail_count >= (n)) | |
1198 | #endif | |
1199 | ||
1200 | void run_one_test() | |
1201 | { | |
1202 | char *x; | |
1203 | # ifdef LINT | |
1204 | char *y = 0; | |
1205 | # else | |
1206 | char *y = (char *)(size_t)fail_proc1; | |
1207 | # endif | |
1208 | DCL_LOCK_STATE; | |
1209 | ||
1210 | # ifdef FIND_LEAK | |
1211 | (void)GC_printf0( | |
1212 | "This test program is not designed for leak detection mode\n"); | |
1213 | (void)GC_printf0("Expect lots of problems.\n"); | |
1214 | # endif | |
1215 | GC_FREE(0); | |
1216 | # ifndef DBG_HDRS_ALL | |
1217 | collectable_count += 3; | |
1218 | if (GC_size(GC_malloc(7)) != 8 && | |
1219 | GC_size(GC_malloc(7)) != MIN_WORDS * sizeof(GC_word) | |
1220 | || GC_size(GC_malloc(15)) != 16) { | |
1221 | (void)GC_printf0("GC_size produced unexpected results\n"); | |
1222 | FAIL; | |
1223 | } | |
1224 | collectable_count += 1; | |
1225 | if (GC_size(GC_malloc(0)) != MIN_WORDS * sizeof(GC_word)) { | |
1226 | (void)GC_printf1("GC_malloc(0) failed: GC_size returns %ld\n", | |
1227 | GC_size(GC_malloc(0))); | |
1228 | FAIL; | |
1229 | } | |
1230 | collectable_count += 1; | |
1231 | if (GC_size(GC_malloc_uncollectable(0)) != MIN_WORDS * sizeof(GC_word)) { | |
1232 | (void)GC_printf0("GC_malloc_uncollectable(0) failed\n"); | |
1233 | FAIL; | |
1234 | } | |
1235 | GC_is_valid_displacement_print_proc = fail_proc1; | |
1236 | GC_is_visible_print_proc = fail_proc1; | |
1237 | collectable_count += 1; | |
1238 | x = GC_malloc(16); | |
1239 | if (GC_base(x + 13) != x) { | |
1240 | (void)GC_printf0("GC_base(heap ptr) produced incorrect result\n"); | |
1241 | FAIL; | |
1242 | } | |
1243 | # ifndef PCR | |
1244 | if (GC_base(y) != 0) { | |
1245 | (void)GC_printf0("GC_base(fn_ptr) produced incorrect result\n"); | |
1246 | FAIL; | |
1247 | } | |
1248 | # endif | |
1249 | if (GC_same_obj(x+5, x) != x + 5) { | |
1250 | (void)GC_printf0("GC_same_obj produced incorrect result\n"); | |
1251 | FAIL; | |
1252 | } | |
1253 | if (GC_is_visible(y) != y || GC_is_visible(x) != x) { | |
1254 | (void)GC_printf0("GC_is_visible produced incorrect result\n"); | |
1255 | FAIL; | |
1256 | } | |
1257 | if (!TEST_FAIL_COUNT(1)) { | |
4109fe85 | 1258 | # if!(defined(RS6000) || defined(POWERPC) || defined(IA64)) || defined(M68K) |
f54d4287 BM |
1259 | /* ON RS6000s function pointers point to a descriptor in the */ |
1260 | /* data segment, so there should have been no failures. */ | |
4109fe85 BM |
1261 | /* The same applies to IA64. Something similar seems to */ |
1262 | /* be going on with NetBSD/M68K. */ | |
f54d4287 BM |
1263 | (void)GC_printf0("GC_is_visible produced wrong failure indication\n"); |
1264 | FAIL; | |
1265 | # endif | |
1266 | } | |
1267 | if (GC_is_valid_displacement(y) != y | |
1268 | || GC_is_valid_displacement(x) != x | |
1269 | || GC_is_valid_displacement(x + 3) != x + 3) { | |
1270 | (void)GC_printf0( | |
1271 | "GC_is_valid_displacement produced incorrect result\n"); | |
1272 | FAIL; | |
1273 | } | |
30c3de1f JS |
1274 | # if defined(__STDC__) && !defined(MSWIN32) && !defined(MSWINCE) |
1275 | /* Harder to test under Windows without a gc.h declaration. */ | |
1276 | { | |
1277 | size_t i; | |
1278 | extern void *GC_memalign(); | |
1279 | ||
1280 | GC_malloc(17); | |
1281 | for (i = sizeof(GC_word); i < 512; i *= 2) { | |
1282 | GC_word result = (GC_word) GC_memalign(i, 17); | |
1283 | if (result % i != 0 || result == 0 || *(int *)result != 0) FAIL; | |
1284 | } | |
1285 | } | |
1286 | # endif | |
f54d4287 BM |
1287 | # ifndef ALL_INTERIOR_POINTERS |
1288 | # if defined(RS6000) || defined(POWERPC) | |
1289 | if (!TEST_FAIL_COUNT(1)) { | |
1290 | # else | |
1291 | if (GC_all_interior_pointers && !TEST_FAIL_COUNT(1) | |
1292 | || !GC_all_interior_pointers && !TEST_FAIL_COUNT(2)) { | |
1293 | # endif | |
1294 | (void)GC_printf0("GC_is_valid_displacement produced wrong failure indication\n"); | |
1295 | FAIL; | |
1296 | } | |
1297 | # endif | |
1298 | # endif /* DBG_HDRS_ALL */ | |
1299 | /* Test floating point alignment */ | |
1300 | collectable_count += 2; | |
1301 | *(double *)GC_MALLOC(sizeof(double)) = 1.0; | |
1302 | *(double *)GC_MALLOC(sizeof(double)) = 1.0; | |
1303 | # ifdef GC_GCJ_SUPPORT | |
1304 | GC_REGISTER_DISPLACEMENT(sizeof(struct fake_vtable *)); | |
1305 | GC_init_gcj_malloc(0, (void *)fake_gcj_mark_proc); | |
30c3de1f JS |
1306 | # endif |
1307 | /* Make sure that fn arguments are visible to the collector. */ | |
1308 | # ifdef __STDC__ | |
1309 | uniq( | |
1310 | GC_malloc(12), GC_malloc(12), GC_malloc(12), | |
1311 | (GC_gcollect(),GC_malloc(12)), | |
1312 | GC_malloc(12), GC_malloc(12), GC_malloc(12), | |
1313 | (GC_gcollect(),GC_malloc(12)), | |
1314 | GC_malloc(12), GC_malloc(12), GC_malloc(12), | |
1315 | (GC_gcollect(),GC_malloc(12)), | |
1316 | GC_malloc(12), GC_malloc(12), GC_malloc(12), | |
1317 | (GC_gcollect(),GC_malloc(12)), | |
1318 | GC_malloc(12), GC_malloc(12), GC_malloc(12), | |
1319 | (GC_gcollect(),GC_malloc(12)), | |
1320 | (void *)0); | |
f54d4287 BM |
1321 | # endif |
1322 | /* Repeated list reversal test. */ | |
1323 | reverse_test(); | |
1324 | # ifdef PRINTSTATS | |
1325 | GC_printf0("-------------Finished reverse_test\n"); | |
1326 | # endif | |
1327 | # ifndef DBG_HDRS_ALL | |
1328 | typed_test(); | |
1329 | # ifdef PRINTSTATS | |
1330 | GC_printf0("-------------Finished typed_test\n"); | |
1331 | # endif | |
1332 | # endif /* DBG_HDRS_ALL */ | |
1333 | tree_test(); | |
1334 | LOCK(); | |
1335 | n_tests++; | |
1336 | UNLOCK(); | |
30c3de1f JS |
1337 | # if defined(THREADS) && defined(HANDLE_FORK) |
1338 | if (fork() == 0) { | |
1339 | GC_gcollect(); | |
1340 | tiny_reverse_test(0); | |
1341 | GC_gcollect(); | |
1342 | GC_printf0("Finished a child process\n"); | |
1343 | exit(0); | |
1344 | } | |
1345 | # endif | |
f54d4287 BM |
1346 | /* GC_printf1("Finished %x\n", pthread_self()); */ |
1347 | } | |
1348 | ||
1349 | void check_heap_stats() | |
1350 | { | |
1351 | unsigned long max_heap_sz; | |
1352 | register int i; | |
1353 | int still_live; | |
1354 | int late_finalize_count = 0; | |
1355 | ||
1356 | # ifdef VERY_SMALL_CONFIG | |
1357 | /* these are something of a guess */ | |
1358 | if (sizeof(char *) > 4) { | |
1359 | max_heap_sz = 4500000; | |
1360 | } else { | |
1361 | max_heap_sz = 2800000; | |
1362 | } | |
1363 | # else | |
1364 | if (sizeof(char *) > 4) { | |
30c3de1f | 1365 | max_heap_sz = 19000000; |
f54d4287 BM |
1366 | } else { |
1367 | max_heap_sz = 11000000; | |
1368 | } | |
1369 | # endif | |
1370 | # ifdef GC_DEBUG | |
1371 | max_heap_sz *= 2; | |
1372 | # ifdef SAVE_CALL_CHAIN | |
1373 | max_heap_sz *= 3; | |
1374 | # ifdef SAVE_CALL_COUNT | |
30c3de1f | 1375 | max_heap_sz += max_heap_sz * SAVE_CALL_COUNT/4; |
f54d4287 BM |
1376 | # endif |
1377 | # endif | |
1378 | # endif | |
1379 | /* Garbage collect repeatedly so that all inaccessible objects */ | |
1380 | /* can be finalized. */ | |
1381 | while (GC_collect_a_little()) { } | |
1382 | for (i = 0; i < 16; i++) { | |
1383 | GC_gcollect(); | |
1384 | late_finalize_count += GC_invoke_finalizers(); | |
1385 | } | |
1386 | (void)GC_printf1("Completed %lu tests\n", (unsigned long)n_tests); | |
1387 | (void)GC_printf1("Allocated %lu collectable objects\n", (unsigned long)collectable_count); | |
1388 | (void)GC_printf1("Allocated %lu uncollectable objects\n", (unsigned long)uncollectable_count); | |
1389 | (void)GC_printf1("Allocated %lu atomic objects\n", (unsigned long)atomic_count); | |
1390 | (void)GC_printf1("Allocated %lu stubborn objects\n", (unsigned long)stubborn_count); | |
1391 | (void)GC_printf2("Finalized %lu/%lu objects - ", | |
1392 | (unsigned long)finalized_count, | |
1393 | (unsigned long)finalizable_count); | |
1394 | # ifdef FINALIZE_ON_DEMAND | |
1395 | if (finalized_count != late_finalize_count) { | |
1396 | (void)GC_printf0("Demand finalization error\n"); | |
1397 | FAIL; | |
1398 | } | |
1399 | # endif | |
1400 | if (finalized_count > finalizable_count | |
1401 | || finalized_count < finalizable_count/2) { | |
1402 | (void)GC_printf0("finalization is probably broken\n"); | |
1403 | FAIL; | |
1404 | } else { | |
1405 | (void)GC_printf0("finalization is probably ok\n"); | |
1406 | } | |
1407 | still_live = 0; | |
1408 | for (i = 0; i < MAX_FINALIZED; i++) { | |
1409 | if (live_indicators[i] != 0) { | |
1410 | still_live++; | |
1411 | } | |
1412 | } | |
1413 | i = finalizable_count - finalized_count - still_live; | |
1414 | if (0 != i) { | |
1415 | (void)GC_printf2 | |
1416 | ("%lu disappearing links remain and %ld more objects were not finalized\n", | |
1417 | (unsigned long) still_live, (long)i); | |
1418 | if (i > 10) { | |
1419 | GC_printf0("\tVery suspicious!\n"); | |
1420 | } else { | |
1421 | GC_printf0("\tSlightly suspicious, but probably OK.\n"); | |
1422 | } | |
1423 | } | |
1424 | (void)GC_printf1("Total number of bytes allocated is %lu\n", | |
1425 | (unsigned long) | |
1426 | WORDS_TO_BYTES(GC_words_allocd + GC_words_allocd_before_gc)); | |
1427 | (void)GC_printf1("Final heap size is %lu bytes\n", | |
1428 | (unsigned long)GC_get_heap_size()); | |
1429 | if (WORDS_TO_BYTES(GC_words_allocd + GC_words_allocd_before_gc) | |
1430 | # ifdef VERY_SMALL_CONFIG | |
1431 | < 2700000*n_tests) { | |
1432 | # else | |
1433 | < 33500000*n_tests) { | |
1434 | # endif | |
1435 | (void)GC_printf0("Incorrect execution - missed some allocations\n"); | |
1436 | FAIL; | |
1437 | } | |
1438 | if (GC_get_heap_size() > max_heap_sz*n_tests) { | |
1439 | (void)GC_printf0("Unexpected heap growth - collector may be broken\n"); | |
1440 | FAIL; | |
1441 | } | |
1442 | (void)GC_printf0("Collector appears to work\n"); | |
1443 | } | |
1444 | ||
1445 | #if defined(MACOS) | |
1446 | void SetMinimumStack(long minSize) | |
1447 | { | |
1448 | long newApplLimit; | |
1449 | ||
1450 | if (minSize > LMGetDefltStack()) | |
1451 | { | |
1452 | newApplLimit = (long) GetApplLimit() | |
1453 | - (minSize - LMGetDefltStack()); | |
1454 | SetApplLimit((Ptr) newApplLimit); | |
1455 | MaxApplZone(); | |
1456 | } | |
1457 | } | |
1458 | ||
1459 | #define cMinStackSpace (512L * 1024L) | |
1460 | ||
1461 | #endif | |
1462 | ||
1463 | #ifdef __STDC__ | |
1464 | void warn_proc(char *msg, GC_word p) | |
1465 | #else | |
1466 | void warn_proc(msg, p) | |
1467 | char *msg; | |
1468 | GC_word p; | |
1469 | #endif | |
1470 | { | |
1471 | GC_printf1(msg, (unsigned long)p); | |
1472 | /*FAIL;*/ | |
1473 | } | |
1474 | ||
1475 | ||
1476 | #if !defined(PCR) && !defined(GC_SOLARIS_THREADS) \ | |
4c7726b1 BM |
1477 | && !defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS) \ |
1478 | || defined(LINT) | |
f54d4287 BM |
1479 | #if defined(MSWIN32) && !defined(__MINGW32__) |
1480 | int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prev, LPTSTR cmd, int n) | |
1481 | #else | |
1482 | int main() | |
1483 | #endif | |
1484 | { | |
1485 | # if defined(DJGPP) | |
1486 | int dummy; | |
1487 | # endif | |
1488 | n_tests = 0; | |
1489 | ||
1490 | # if defined(DJGPP) | |
1491 | /* No good way to determine stack base from library; do it */ | |
1492 | /* manually on this platform. */ | |
1493 | GC_stackbottom = (GC_PTR)(&dummy); | |
1494 | # endif | |
1495 | # if defined(MACOS) | |
1496 | /* Make sure we have lots and lots of stack space. */ | |
1497 | SetMinimumStack(cMinStackSpace); | |
1498 | /* Cheat and let stdio initialize toolbox for us. */ | |
1499 | printf("Testing GC Macintosh port.\n"); | |
1500 | # endif | |
4109fe85 | 1501 | GC_INIT(); /* Only needed on a few platforms. */ |
f54d4287 | 1502 | (void) GC_set_warn_proc(warn_proc); |
30c3de1f JS |
1503 | # if (defined(MPROTECT_VDB) || defined(PROC_VDB)) \ |
1504 | && !defined(MAKE_BACK_GRAPH) | |
f54d4287 BM |
1505 | GC_enable_incremental(); |
1506 | (void) GC_printf0("Switched to incremental mode\n"); | |
1507 | # if defined(MPROTECT_VDB) | |
1508 | (void)GC_printf0("Emulating dirty bits with mprotect/signals\n"); | |
1509 | # else | |
30c3de1f | 1510 | # ifdef PROC_VDB |
f54d4287 | 1511 | (void)GC_printf0("Reading dirty bits from /proc\n"); |
30c3de1f JS |
1512 | # else |
1513 | (void)GC_printf0("Using DEFAULT_VDB dirty bit implementation\n"); | |
1514 | # endif | |
f54d4287 BM |
1515 | # endif |
1516 | # endif | |
1517 | run_one_test(); | |
1518 | check_heap_stats(); | |
1519 | # ifndef MSWINCE | |
1520 | (void)fflush(stdout); | |
1521 | # endif | |
1522 | # ifdef LINT | |
1523 | /* Entry points we should be testing, but aren't. */ | |
1524 | /* Some can be tested by defining GC_DEBUG at the top of this file */ | |
1525 | /* This is a bit SunOS4 specific. */ | |
1526 | GC_noop(GC_expand_hp, GC_add_roots, GC_clear_roots, | |
1527 | GC_register_disappearing_link, | |
1528 | GC_register_finalizer_ignore_self, | |
1529 | GC_debug_register_displacement, | |
1530 | GC_print_obj, GC_debug_change_stubborn, | |
1531 | GC_debug_end_stubborn_change, GC_debug_malloc_uncollectable, | |
1532 | GC_debug_free, GC_debug_realloc, GC_generic_malloc_words_small, | |
1533 | GC_init, GC_make_closure, GC_debug_invoke_finalizer, | |
1534 | GC_page_was_ever_dirty, GC_is_fresh, | |
1535 | GC_malloc_ignore_off_page, GC_malloc_atomic_ignore_off_page, | |
1536 | GC_set_max_heap_size, GC_get_bytes_since_gc, | |
1537 | GC_get_total_bytes, GC_pre_incr, GC_post_incr); | |
1538 | # endif | |
1539 | # ifdef MSWIN32 | |
1540 | GC_win32_free_heap(); | |
1541 | # endif | |
1542 | return(0); | |
1543 | } | |
1544 | # endif | |
1545 | ||
30c3de1f | 1546 | #if defined(GC_WIN32_THREADS) && !defined(CYGWIN32) |
f54d4287 | 1547 | |
30c3de1f | 1548 | DWORD __stdcall thr_run_one_test(void *arg) |
f54d4287 BM |
1549 | { |
1550 | run_one_test(); | |
1551 | return 0; | |
1552 | } | |
1553 | ||
1554 | #ifdef MSWINCE | |
1555 | HANDLE win_created_h; | |
1556 | HWND win_handle; | |
1557 | ||
1558 | LRESULT CALLBACK window_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) | |
1559 | { | |
1560 | LRESULT ret = 0; | |
1561 | switch (uMsg) { | |
1562 | case WM_HIBERNATE: | |
1563 | GC_printf0("Received WM_HIBERNATE, calling GC_gcollect\n"); | |
1564 | GC_gcollect(); | |
1565 | break; | |
1566 | case WM_CLOSE: | |
1567 | GC_printf0("Received WM_CLOSE, closing window\n"); | |
1568 | DestroyWindow(hwnd); | |
1569 | break; | |
1570 | case WM_DESTROY: | |
1571 | PostQuitMessage(0); | |
1572 | break; | |
1573 | default: | |
1574 | ret = DefWindowProc(hwnd, uMsg, wParam, lParam); | |
1575 | break; | |
1576 | } | |
1577 | return ret; | |
1578 | } | |
1579 | ||
30c3de1f | 1580 | DWORD __stdcall thr_window(void *arg) |
f54d4287 BM |
1581 | { |
1582 | WNDCLASS win_class = { | |
1583 | CS_NOCLOSE, | |
1584 | window_proc, | |
1585 | 0, | |
1586 | 0, | |
1587 | GetModuleHandle(NULL), | |
1588 | NULL, | |
1589 | NULL, | |
1590 | (HBRUSH)(COLOR_APPWORKSPACE+1), | |
1591 | NULL, | |
1592 | L"GCtestWindow" | |
1593 | }; | |
1594 | MSG msg; | |
1595 | ||
1596 | if (!RegisterClass(&win_class)) | |
1597 | FAIL; | |
1598 | ||
1599 | win_handle = CreateWindowEx( | |
1600 | 0, | |
1601 | L"GCtestWindow", | |
1602 | L"GCtest", | |
1603 | 0, | |
1604 | CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, | |
1605 | NULL, | |
1606 | NULL, | |
1607 | GetModuleHandle(NULL), | |
1608 | NULL); | |
1609 | ||
1610 | if (win_handle == NULL) | |
1611 | FAIL; | |
1612 | ||
1613 | SetEvent(win_created_h); | |
1614 | ||
1615 | ShowWindow(win_handle, SW_SHOW); | |
1616 | UpdateWindow(win_handle); | |
1617 | ||
1618 | while (GetMessage(&msg, NULL, 0, 0)) { | |
1619 | TranslateMessage(&msg); | |
1620 | DispatchMessage(&msg); | |
1621 | } | |
1622 | ||
1623 | return 0; | |
1624 | } | |
1625 | #endif | |
1626 | ||
1627 | #define NTEST 2 | |
1628 | ||
1629 | # ifdef MSWINCE | |
1630 | int APIENTRY GC_WinMain(HINSTANCE instance, HINSTANCE prev, LPWSTR cmd, int n) | |
1631 | # else | |
1632 | int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prev, LPSTR cmd, int n) | |
1633 | # endif | |
1634 | { | |
1635 | # if NTEST > 0 | |
1636 | HANDLE h[NTEST]; | |
1637 | int i; | |
1638 | # endif | |
1639 | # ifdef MSWINCE | |
1640 | HANDLE win_thr_h; | |
1641 | # endif | |
30c3de1f | 1642 | DWORD thread_id; |
f54d4287 BM |
1643 | # if 0 |
1644 | GC_enable_incremental(); | |
1645 | # endif | |
30c3de1f | 1646 | GC_init(); |
f54d4287 BM |
1647 | InitializeCriticalSection(&incr_cs); |
1648 | (void) GC_set_warn_proc(warn_proc); | |
1649 | # ifdef MSWINCE | |
1650 | win_created_h = CreateEvent(NULL, FALSE, FALSE, NULL); | |
1651 | if (win_created_h == (HANDLE)NULL) { | |
1652 | (void)GC_printf1("Event creation failed %lu\n", (unsigned long)GetLastError()); | |
1653 | FAIL; | |
1654 | } | |
1655 | win_thr_h = GC_CreateThread(NULL, 0, thr_window, 0, 0, &thread_id); | |
1656 | if (win_thr_h == (HANDLE)NULL) { | |
1657 | (void)GC_printf1("Thread creation failed %lu\n", (unsigned long)GetLastError()); | |
1658 | FAIL; | |
1659 | } | |
1660 | if (WaitForSingleObject(win_created_h, INFINITE) != WAIT_OBJECT_0) | |
1661 | FAIL; | |
1662 | CloseHandle(win_created_h); | |
1663 | # endif | |
1664 | # if NTEST > 0 | |
1665 | for (i = 0; i < NTEST; i++) { | |
1666 | h[i] = GC_CreateThread(NULL, 0, thr_run_one_test, 0, 0, &thread_id); | |
1667 | if (h[i] == (HANDLE)NULL) { | |
1668 | (void)GC_printf1("Thread creation failed %lu\n", (unsigned long)GetLastError()); | |
1669 | FAIL; | |
1670 | } | |
1671 | } | |
1672 | # endif /* NTEST > 0 */ | |
1673 | run_one_test(); | |
1674 | # if NTEST > 0 | |
1675 | for (i = 0; i < NTEST; i++) { | |
1676 | if (WaitForSingleObject(h[i], INFINITE) != WAIT_OBJECT_0) { | |
1677 | (void)GC_printf1("Thread wait failed %lu\n", (unsigned long)GetLastError()); | |
1678 | FAIL; | |
1679 | } | |
1680 | } | |
1681 | # endif /* NTEST > 0 */ | |
1682 | # ifdef MSWINCE | |
1683 | PostMessage(win_handle, WM_CLOSE, 0, 0); | |
1684 | if (WaitForSingleObject(win_thr_h, INFINITE) != WAIT_OBJECT_0) | |
1685 | FAIL; | |
1686 | # endif | |
1687 | check_heap_stats(); | |
1688 | return(0); | |
1689 | } | |
1690 | ||
1691 | #endif /* GC_WIN32_THREADS */ | |
1692 | ||
1693 | ||
1694 | #ifdef PCR | |
1695 | test() | |
1696 | { | |
1697 | PCR_Th_T * th1; | |
1698 | PCR_Th_T * th2; | |
1699 | int code; | |
1700 | ||
1701 | n_tests = 0; | |
1702 | /* GC_enable_incremental(); */ | |
1703 | (void) GC_set_warn_proc(warn_proc); | |
1704 | th1 = PCR_Th_Fork(run_one_test, 0); | |
1705 | th2 = PCR_Th_Fork(run_one_test, 0); | |
1706 | run_one_test(); | |
1707 | if (PCR_Th_T_Join(th1, &code, NIL, PCR_allSigsBlocked, PCR_waitForever) | |
1708 | != PCR_ERes_okay || code != 0) { | |
1709 | (void)GC_printf0("Thread 1 failed\n"); | |
1710 | } | |
1711 | if (PCR_Th_T_Join(th2, &code, NIL, PCR_allSigsBlocked, PCR_waitForever) | |
1712 | != PCR_ERes_okay || code != 0) { | |
1713 | (void)GC_printf0("Thread 2 failed\n"); | |
1714 | } | |
1715 | check_heap_stats(); | |
1716 | return(0); | |
1717 | } | |
1718 | #endif | |
1719 | ||
4c7726b1 | 1720 | #if defined(GC_SOLARIS_THREADS) || defined(GC_PTHREADS) |
f54d4287 BM |
1721 | void * thr_run_one_test(void * arg) |
1722 | { | |
1723 | run_one_test(); | |
1724 | return(0); | |
1725 | } | |
1726 | ||
1727 | #ifdef GC_DEBUG | |
1728 | # define GC_free GC_debug_free | |
1729 | #endif | |
1730 | ||
4c7726b1 | 1731 | #if defined(GC_SOLARIS_THREADS) && !defined(GC_SOLARIS_PTHREADS) |
f54d4287 BM |
1732 | main() |
1733 | { | |
1734 | thread_t th1; | |
1735 | thread_t th2; | |
1736 | int code; | |
1737 | ||
1738 | n_tests = 0; | |
1739 | GC_INIT(); /* Only needed if gc is dynamic library. */ | |
79f777fd BM |
1740 | # ifndef MAKE_BACK_GRAPH |
1741 | GC_enable_incremental(); | |
1742 | # endif | |
f54d4287 BM |
1743 | (void) GC_set_warn_proc(warn_proc); |
1744 | if (thr_keycreate(&fl_key, GC_free) != 0) { | |
1745 | (void)GC_printf1("Key creation failed %lu\n", (unsigned long)code); | |
1746 | FAIL; | |
1747 | } | |
1748 | if ((code = thr_create(0, 1024*1024, thr_run_one_test, 0, 0, &th1)) != 0) { | |
1749 | (void)GC_printf1("Thread 1 creation failed %lu\n", (unsigned long)code); | |
1750 | FAIL; | |
1751 | } | |
1752 | if ((code = thr_create(0, 1024*1024, thr_run_one_test, 0, THR_NEW_LWP, &th2)) != 0) { | |
1753 | (void)GC_printf1("Thread 2 creation failed %lu\n", (unsigned long)code); | |
1754 | FAIL; | |
1755 | } | |
1756 | run_one_test(); | |
1757 | if ((code = thr_join(th1, 0, 0)) != 0) { | |
1758 | (void)GC_printf1("Thread 1 failed %lu\n", (unsigned long)code); | |
1759 | FAIL; | |
1760 | } | |
1761 | if (thr_join(th2, 0, 0) != 0) { | |
1762 | (void)GC_printf1("Thread 2 failed %lu\n", (unsigned long)code); | |
1763 | FAIL; | |
1764 | } | |
1765 | check_heap_stats(); | |
1766 | (void)fflush(stdout); | |
1767 | return(0); | |
1768 | } | |
1769 | #else /* pthreads */ | |
4c7726b1 BM |
1770 | |
1771 | #ifndef GC_PTHREADS | |
1772 | --> bad news | |
1773 | #endif | |
1774 | ||
f54d4287 BM |
1775 | main() |
1776 | { | |
1777 | pthread_t th1; | |
1778 | pthread_t th2; | |
1779 | pthread_attr_t attr; | |
1780 | int code; | |
1781 | ||
1782 | # ifdef GC_IRIX_THREADS | |
1783 | /* Force a larger stack to be preallocated */ | |
1784 | /* Since the initial cant always grow later. */ | |
1785 | *((volatile char *)&code - 1024*1024) = 0; /* Require 1 Mb */ | |
1786 | # endif /* GC_IRIX_THREADS */ | |
4c7726b1 BM |
1787 | # if defined(GC_HPUX_THREADS) |
1788 | /* Default stack size is too small, especially with the 64 bit ABI */ | |
1789 | /* Increase it. */ | |
1790 | if (pthread_default_stacksize_np(1024*1024, 0) != 0) { | |
1791 | (void)GC_printf0("pthread_default_stacksize_np failed.\n"); | |
1792 | } | |
1793 | # endif /* GC_HPUX_THREADS */ | |
4109fe85 | 1794 | GC_INIT(); |
30c3de1f | 1795 | |
f54d4287 | 1796 | pthread_attr_init(&attr); |
30c3de1f JS |
1797 | # if defined(GC_IRIX_THREADS) || defined(GC_FREEBSD_THREADS) \ |
1798 | || defined(GC_DARWIN_THREADS) || defined(GC_AIX_THREADS) | |
f54d4287 BM |
1799 | pthread_attr_setstacksize(&attr, 1000000); |
1800 | # endif | |
1801 | n_tests = 0; | |
30c3de1f JS |
1802 | # if (defined(MPROTECT_VDB)) \ |
1803 | && !defined(PARALLEL_MARK) &&!defined(REDIRECT_MALLOC) \ | |
1804 | && !defined(MAKE_BACK_GRAPH) | |
f54d4287 BM |
1805 | GC_enable_incremental(); |
1806 | (void) GC_printf0("Switched to incremental mode\n"); | |
30c3de1f JS |
1807 | # if defined(MPROTECT_VDB) |
1808 | (void)GC_printf0("Emulating dirty bits with mprotect/signals\n"); | |
1809 | # else | |
1810 | # ifdef PROC_VDB | |
1811 | (void)GC_printf0("Reading dirty bits from /proc\n"); | |
1812 | # else | |
1813 | (void)GC_printf0("Using DEFAULT_VDB dirty bit implementation\n"); | |
1814 | # endif | |
1815 | # endif | |
f54d4287 BM |
1816 | # endif |
1817 | (void) GC_set_warn_proc(warn_proc); | |
1818 | if ((code = pthread_key_create(&fl_key, 0)) != 0) { | |
1819 | (void)GC_printf1("Key creation failed %lu\n", (unsigned long)code); | |
1820 | FAIL; | |
1821 | } | |
1822 | if ((code = pthread_create(&th1, &attr, thr_run_one_test, 0)) != 0) { | |
1823 | (void)GC_printf1("Thread 1 creation failed %lu\n", (unsigned long)code); | |
1824 | FAIL; | |
1825 | } | |
1826 | if ((code = pthread_create(&th2, &attr, thr_run_one_test, 0)) != 0) { | |
1827 | (void)GC_printf1("Thread 2 creation failed %lu\n", (unsigned long)code); | |
1828 | FAIL; | |
1829 | } | |
1830 | run_one_test(); | |
1831 | if ((code = pthread_join(th1, 0)) != 0) { | |
1832 | (void)GC_printf1("Thread 1 failed %lu\n", (unsigned long)code); | |
1833 | FAIL; | |
1834 | } | |
1835 | if (pthread_join(th2, 0) != 0) { | |
1836 | (void)GC_printf1("Thread 2 failed %lu\n", (unsigned long)code); | |
1837 | FAIL; | |
1838 | } | |
1839 | check_heap_stats(); | |
1840 | (void)fflush(stdout); | |
1841 | pthread_attr_destroy(&attr); | |
1842 | GC_printf1("Completed %d collections\n", GC_gc_no); | |
1843 | return(0); | |
1844 | } | |
4c7726b1 BM |
1845 | #endif /* GC_PTHREADS */ |
1846 | #endif /* GC_SOLARIS_THREADS || GC_PTHREADS */ |