Synopsis - Cross-Reference
File: /src/Synopsis/gc/tests/test.c1/* 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. */ 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. */ 20 21# undef GC_BUILD 22 23#if defined(DBG_HDRS_ALL) || defined(MAKE_BACK_GRAPH) 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 "gc.h" 40# include "gc_typed.h" 41# include "private/gc_priv.h" /* For output, locking, MIN_WORDS, */ 42 /* and some statistics, and gcconfig.h. */ 43 44# if defined(MSWIN32) || defined(MSWINCE) 45# include <windows.h> 46# endif 47 48# ifdef PCR 49# include "th/PCR_ThCrSec.h" 50# include "th/PCR_Th.h" 51# define GC_printf printf 52# endif 53 54# if defined(GC_PTHREADS) 55# include <pthread.h> 56# endif 57 58# if defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS) 59 static CRITICAL_SECTION incr_cs; 60# endif 61 62#ifdef __STDC__ 63# include <stdarg.h> 64#endif 65 66 67/* Allocation Statistics */ 68int stubborn_count = 0; 69int uncollectable_count = 0; 70int collectable_count = 0; 71int atomic_count = 0; 72int realloc_count = 0; 73 74#if defined(GC_AMIGA_FASTALLOC) && defined(AMIGA) 75 76 extern void GC_amiga_free_all_mem(void); 77 void Amiga_Fail(void){GC_amiga_free_all_mem();abort();} 78# define FAIL (void)Amiga_Fail() 79 void *GC_amiga_gctest_malloc_explicitly_typed(size_t lb, GC_descr d){ 80 void *ret=GC_malloc_explicitly_typed(lb,d); 81 if(ret==NULL){ 82 if(!GC_dont_gc){ 83 GC_gcollect(); 84 ret=GC_malloc_explicitly_typed(lb,d); 85 } 86 if(ret==NULL){ 87 GC_printf("Out of memory, (typed allocations are not directly " 88 "supported with the GC_AMIGA_FASTALLOC option.)\n"); 89 FAIL; 90 } 91 } 92 return ret; 93 } 94 void *GC_amiga_gctest_calloc_explicitly_typed(size_t a,size_t lb, GC_descr d){ 95 void *ret=GC_calloc_explicitly_typed(a,lb,d); 96 if(ret==NULL){ 97 if(!GC_dont_gc){ 98 GC_gcollect(); 99 ret=GC_calloc_explicitly_typed(a,lb,d); 100 } 101 if(ret==NULL){ 102 GC_printf("Out of memory, (typed allocations are not directly " 103 "supported with the GC_AMIGA_FASTALLOC option.)\n"); 104 FAIL; 105 } 106 } 107 return ret; 108 } 109# define GC_malloc_explicitly_typed(a,b) GC_amiga_gctest_malloc_explicitly_typed(a,b) 110# define GC_calloc_explicitly_typed(a,b,c) GC_amiga_gctest_calloc_explicitly_typed(a,b,c) 111 112#else /* !AMIGA_FASTALLOC */ 113 114# ifdef PCR 115# define FAIL (void)abort() 116# else 117# ifdef MSWINCE 118# define FAIL DebugBreak() 119# else 120# define FAIL GC_abort("Test failed"); 121# endif 122# endif 123 124#endif /* !AMIGA_FASTALLOC */ 125 126/* AT_END may be defined to exercise the interior pointer test */ 127/* if the collector is configured with ALL_INTERIOR_POINTERS. */ 128/* As it stands, this test should succeed with either */ 129/* configuration. In the FIND_LEAK configuration, it should */ 130/* find lots of leaks, since we free almost nothing. */ 131 132struct SEXPR { 133 struct SEXPR * sexpr_car; 134 struct SEXPR * sexpr_cdr; 135}; 136 137 138typedef struct SEXPR * sexpr; 139 140# define INT_TO_SEXPR(x) ((sexpr)(GC_word)(x)) 141# define SEXPR_TO_INT(x) ((int)(GC_word)(x)) 142 143# undef nil 144# define nil (INT_TO_SEXPR(0)) 145# define car(x) ((x) -> sexpr_car) 146# define cdr(x) ((x) -> sexpr_cdr) 147# define is_nil(x) ((x) == nil) 148 149 150int extra_count = 0; /* Amount of space wasted in cons node */ 151 152/* Silly implementation of Lisp cons. Intentionally wastes lots of space */ 153/* to test collector. */ 154# ifdef VERY_SMALL_CONFIG 155# define cons small_cons 156# else 157sexpr cons (sexpr x, sexpr y) 158{ 159 sexpr r; 160 int *p; 161 int my_extra = extra_count; 162 163 stubborn_count++; 164 r = (sexpr) GC_MALLOC_STUBBORN(sizeof(struct SEXPR) + my_extra); 165 if (r == 0) { 166 (void)GC_printf("Out of memory\n"); 167 exit(1); 168 } 169 for (p = (int *)r; 170 ((char *)p) < ((char *)r) + my_extra + sizeof(struct SEXPR); p++) { 171 if (*p) { 172 (void)GC_printf("Found nonzero at %p - allocator is broken\n", p); 173 FAIL; 174 } 175 *p = (int)((13 << 12) + ((p - (int *)r) & 0xfff)); 176 } 177# ifdef AT_END 178 r = (sexpr)((char *)r + (my_extra & ~7)); 179# endif 180 r -> sexpr_car = x; 181 r -> sexpr_cdr = y; 182 my_extra++; 183 if ( my_extra >= 5000 ) { 184 extra_count = 0; 185 } else { 186 extra_count = my_extra; 187 } 188 GC_END_STUBBORN_CHANGE((char *)r); 189 return(r); 190} 191# endif 192 193#ifdef GC_GCJ_SUPPORT 194 195#include "gc_mark.h" 196#include "gc_gcj.h" 197 198/* The following struct emulates the vtable in gcj. */ 199/* This assumes the default value of MARK_DESCR_OFFSET. */ 200struct fake_vtable { 201 void * dummy; /* class pointer in real gcj. */ 202 size_t descr; 203}; 204 205struct fake_vtable gcj_class_struct1 = { 0, sizeof(struct SEXPR) 206 + sizeof(struct fake_vtable *) }; 207 /* length based descriptor. */ 208struct fake_vtable gcj_class_struct2 = 209 { 0, (3l << (CPP_WORDSZ - 3)) | GC_DS_BITMAP}; 210 /* Bitmap based descriptor. */ 211 212struct GC_ms_entry * fake_gcj_mark_proc(word * addr, 213 struct GC_ms_entry *mark_stack_ptr, 214 struct GC_ms_entry *mark_stack_limit, 215 word env ) 216{ 217 sexpr x; 218 if (1 == env) { 219 /* Object allocated with debug allocator. */ 220 addr = (word *)GC_USR_PTR_FROM_BASE(addr); 221 } 222 x = (sexpr)(addr + 1); /* Skip the vtable pointer. */ 223 mark_stack_ptr = GC_MARK_AND_PUSH( 224 (void *)(x -> sexpr_cdr), mark_stack_ptr, 225 mark_stack_limit, (void * *)&(x -> sexpr_cdr)); 226 mark_stack_ptr = GC_MARK_AND_PUSH( 227 (void *)(x -> sexpr_car), mark_stack_ptr, 228 mark_stack_limit, (void * *)&(x -> sexpr_car)); 229 return(mark_stack_ptr); 230} 231 232#endif /* GC_GCJ_SUPPORT */ 233 234 235sexpr small_cons (sexpr x, sexpr y) 236{ 237 sexpr r; 238 239 collectable_count++; 240 r = (sexpr) GC_MALLOC(sizeof(struct SEXPR)); 241 if (r == 0) { 242 (void)GC_printf("Out of memory\n"); 243 exit(1); 244 } 245 r -> sexpr_car = x; 246 r -> sexpr_cdr = y; 247 return(r); 248} 249 250sexpr small_cons_uncollectable (sexpr x, sexpr y) 251{ 252 sexpr r; 253 254 uncollectable_count++; 255 r = (sexpr) GC_MALLOC_UNCOLLECTABLE(sizeof(struct SEXPR)); 256 if (r == 0) { 257 (void)GC_printf("Out of memory\n"); 258 exit(1); 259 } 260 r -> sexpr_car = x; 261 r -> sexpr_cdr = (sexpr)(~(GC_word)y); 262 return(r); 263} 264 265#ifdef GC_GCJ_SUPPORT 266 267 268sexpr gcj_cons(sexpr x, sexpr y) 269{ 270 GC_word * r; 271 sexpr result; 272 static int count = 0; 273 274 r = (GC_word *) GC_GCJ_MALLOC(sizeof(struct SEXPR) 275 + sizeof(struct fake_vtable*), 276 &gcj_class_struct2); 277 if (r == 0) { 278 (void)GC_printf("Out of memory\n"); 279 exit(1); 280 } 281 result = (sexpr)(r + 1); 282 result -> sexpr_car = x; 283 result -> sexpr_cdr = y; 284 return(result); 285} 286#endif 287 288/* Return reverse(x) concatenated with y */ 289sexpr reverse1(sexpr x, sexpr y) 290{ 291 if (is_nil(x)) { 292 return(y); 293 } else { 294 return( reverse1(cdr(x), cons(car(x), y)) ); 295 } 296} 297 298sexpr reverse(sexpr x) 299{ 300# ifdef TEST_WITH_SYSTEM_MALLOC 301 malloc(100000); 302# endif 303 return( reverse1(x, nil) ); 304} 305 306sexpr ints(int low, int up) 307{ 308 if (low > up) { 309 return(nil); 310 } else { 311 return(small_cons(small_cons(INT_TO_SEXPR(low), nil), ints(low+1, up))); 312 } 313} 314 315#ifdef GC_GCJ_SUPPORT 316/* Return reverse(x) concatenated with y */ 317sexpr gcj_reverse1(sexpr x, sexpr y) 318{ 319 if (is_nil(x)) { 320 return(y); 321 } else { 322 return( gcj_reverse1(cdr(x), gcj_cons(car(x), y)) ); 323 } 324} 325 326sexpr gcj_reverse(sexpr x) 327{ 328 return( gcj_reverse1(x, nil) ); 329} 330 331sexpr gcj_ints(int low, int up) 332{ 333 if (low > up) { 334 return(nil); 335 } else { 336 return(gcj_cons(gcj_cons(INT_TO_SEXPR(low), nil), gcj_ints(low+1, up))); 337 } 338} 339#endif /* GC_GCJ_SUPPORT */ 340 341/* To check uncollectable allocation we build lists with disguised cdr */ 342/* pointers, and make sure they don't go away. */ 343sexpr uncollectable_ints(int low, int up) 344{ 345 if (low > up) { 346 return(nil); 347 } else { 348 return(small_cons_uncollectable(small_cons(INT_TO_SEXPR(low), nil), 349 uncollectable_ints(low+1, up))); 350 } 351} 352 353void check_ints(sexpr list, int low, int up) 354{ 355 if (SEXPR_TO_INT(car(car(list))) != low) { 356 (void)GC_printf( 357 "List reversal produced incorrect list - collector is broken\n"); 358 FAIL; 359 } 360 if (low == up) { 361 if (cdr(list) != nil) { 362 (void)GC_printf("List too long - collector is broken\n"); 363 FAIL; 364 } 365 } else { 366 check_ints(cdr(list), low+1, up); 367 } 368} 369 370# define UNCOLLECTABLE_CDR(x) (sexpr)(~(GC_word)(cdr(x))) 371 372void check_uncollectable_ints(sexpr list, int low, int up) 373{ 374 if (SEXPR_TO_INT(car(car(list))) != low) { 375 (void)GC_printf( 376 "Uncollectable list corrupted - collector is broken\n"); 377 FAIL; 378 } 379 if (low == up) { 380 if (UNCOLLECTABLE_CDR(list) != nil) { 381 (void)GC_printf("Uncollectable list too long - collector is broken\n"); 382 FAIL; 383 } 384 } else { 385 check_uncollectable_ints(UNCOLLECTABLE_CDR(list), low+1, up); 386 } 387} 388 389/* Not used, but useful for debugging: */ 390void print_int_list(sexpr x) 391{ 392 if (is_nil(x)) { 393 (void)GC_printf("NIL\n"); 394 } else { 395 (void)GC_printf("(%d)", SEXPR_TO_INT(car(car(x)))); 396 if (!is_nil(cdr(x))) { 397 (void)GC_printf(", "); 398 (void)print_int_list(cdr(x)); 399 } else { 400 (void)GC_printf("\n"); 401 } 402 } 403} 404 405/* ditto: */ 406void check_marks_int_list(sexpr x) 407{ 408 if (!GC_is_marked((ptr_t)x)) 409 GC_printf("[unm:%p]", x); 410 else 411 GC_printf("[mkd:%p]", x); 412 if (is_nil(x)) { 413 (void)GC_printf("NIL\n"); 414 } else { 415 if (!GC_is_marked((ptr_t)car(x))) GC_printf("[unm car:%p]", car(x)); 416 (void)GC_printf("(%d)", SEXPR_TO_INT(car(car(x)))); 417 if (!is_nil(cdr(x))) { 418 (void)GC_printf(", "); 419 (void)check_marks_int_list(cdr(x)); 420 } else { 421 (void)GC_printf("\n"); 422 } 423 } 424} 425 426/* 427 * A tiny list reversal test to check thread creation. 428 */ 429#ifdef THREADS 430 431# if defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS) 432 DWORD __stdcall tiny_reverse_test(void * arg) 433# else 434 void * tiny_reverse_test(void * arg) 435# endif 436{ 437 int i; 438 for (i = 0; i < 5; ++i) { 439 check_ints(reverse(reverse(ints(1,10))), 1, 10); 440 } 441 return 0; 442} 443 444# if defined(GC_PTHREADS) 445 void fork_a_thread() 446 { 447 pthread_t t; 448 int code; 449 if ((code = pthread_create(&t, 0, tiny_reverse_test, 0)) != 0) { 450 (void)GC_printf("Small thread creation failed %d\n", code); 451 FAIL; 452 } 453 if ((code = pthread_join(t, 0)) != 0) { 454 (void)GC_printf("Small thread join failed %d\n", code); 455 FAIL; 456 } 457 } 458 459# elif defined(GC_WIN32_THREADS) 460 void fork_a_thread() 461 { 462 DWORD thread_id; 463 HANDLE h; 464 h = GC_CreateThread(NULL, 0, tiny_reverse_test, 0, 0, &thread_id); 465 if (h == (HANDLE)NULL) { 466 (void)GC_printf("Small thread creation failed %d\n", 467 GetLastError()); 468 FAIL; 469 } 470 if (WaitForSingleObject(h, INFINITE) != WAIT_OBJECT_0) { 471 (void)GC_printf("Small thread wait failed %d\n", 472 GetLastError()); 473 FAIL; 474 } 475 } 476 477# else 478 479# define fork_a_thread() 480 481# endif 482 483#else 484 485# define fork_a_thread() 486 487#endif 488 489/* Try to force a to be strangely aligned */ 490struct { 491 char dummy; 492 sexpr aa; 493} A; 494#define a A.aa 495 496/* 497 * Repeatedly reverse lists built out of very different sized cons cells. 498 * Check that we didn't lose anything. 499 */ 500void reverse_test() 501{ 502 int i; 503 sexpr b; 504 sexpr c; 505 sexpr d; 506 sexpr e; 507 sexpr *f, *g, *h; 508# if defined(MSWIN32) || defined(MACOS) 509 /* Win32S only allows 128K stacks */ 510# define BIG 1000 511# else 512# if defined PCR 513 /* PCR default stack is 100K. Stack frames are up to 120 bytes. */ 514# define BIG 700 515# else 516# if defined MSWINCE 517 /* WinCE only allows 64K stacks */ 518# define BIG 500 519# else 520# if defined(OSF1) 521 /* OSF has limited stack space by default, and large frames. */ 522# define BIG 200 523# else 524# define BIG 4500 525# endif 526# endif 527# endif 528# endif 529 530 A.dummy = 17; 531 a = ints(1, 49); 532 b = ints(1, 50); 533 c = ints(1, BIG); 534 d = uncollectable_ints(1, 100); 535 e = uncollectable_ints(1, 1); 536 /* Check that realloc updates object descriptors correctly */ 537 collectable_count++; 538 f = (sexpr *)GC_MALLOC(4 * sizeof(sexpr)); 539 realloc_count++; 540 f = (sexpr *)GC_REALLOC((void *)f, 6 * sizeof(sexpr)); 541 f[5] = ints(1,17); 542 collectable_count++; 543 g = (sexpr *)GC_MALLOC(513 * sizeof(sexpr)); 544 realloc_count++; 545 g = (sexpr *)GC_REALLOC((void *)g, 800 * sizeof(sexpr)); 546 g[799] = ints(1,18); 547 collectable_count++; 548 h = (sexpr *)GC_MALLOC(1025 * sizeof(sexpr)); 549 realloc_count++; 550 h = (sexpr *)GC_REALLOC((void *)h, 2000 * sizeof(sexpr)); 551# ifdef GC_GCJ_SUPPORT 552 h[1999] = gcj_ints(1,200); 553 for (i = 0; i < 51; ++i) 554 h[1999] = gcj_reverse(h[1999]); 555 /* Leave it as the reveresed list for now. */ 556# else 557 h[1999] = ints(1,200); 558# endif 559 /* Try to force some collections and reuse of small list elements */ 560 for (i = 0; i < 10; i++) { 561 (void)ints(1, BIG); 562 } 563 /* Superficially test interior pointer recognition on stack */ 564 c = (sexpr)((char *)c + sizeof(char *)); 565 d = (sexpr)((char *)d + sizeof(char *)); 566 567# ifdef __STDC__ 568 GC_FREE((void *)e); 569# else 570 GC_FREE((char *)e); 571# endif 572 check_ints(b,1,50); 573 check_ints(a,1,49); 574 for (i = 0; i < 50; i++) { 575 check_ints(b,1,50); 576 b = reverse(reverse(b)); 577 } 578 check_ints(b,1,50); 579 check_ints(a,1,49); 580 for (i = 0; i < 60; i++) { 581 if (i % 10 == 0) fork_a_thread(); 582 /* This maintains the invariant that a always points to a list of */ 583 /* 49 integers. Thus this is thread safe without locks, */ 584 /* assuming atomic pointer assignments. */ 585 a = reverse(reverse(a)); 586# if !defined(AT_END) && !defined(THREADS) 587 /* This is not thread safe, since realloc explicitly deallocates */ 588 if (i & 1) { 589 a = (sexpr)GC_REALLOC((void *)a, 500); 590 } else { 591 a = (sexpr)GC_REALLOC((void *)a, 8200); 592 } 593# endif 594 } 595 check_ints(a,1,49); 596 check_ints(b,1,50); 597 c = (sexpr)((char *)c - sizeof(char *)); 598 d = (sexpr)((char *)d - sizeof(char *)); 599 check_ints(c,1,BIG); 600 check_uncollectable_ints(d, 1, 100); 601 check_ints(f[5], 1,17); 602 check_ints(g[799], 1,18); 603# ifdef GC_GCJ_SUPPORT 604 h[1999] = gcj_reverse(h[1999]); 605# endif 606 check_ints(h[1999], 1,200); 607# ifndef THREADS 608 a = 0; 609# endif 610 b = c = 0; 611} 612 613#undef a 614 615/* 616 * The rest of this builds balanced binary trees, checks that they don't 617 * disappear, and tests finalization. 618 */ 619typedef struct treenode { 620 int level; 621 struct treenode * lchild; 622 struct treenode * rchild; 623} tn; 624 625int finalizable_count = 0; 626int finalized_count = 0; 627volatile int dropped_something = 0; 628 629# ifdef __STDC__ 630 void finalizer(void * obj, void * client_data) 631# else 632 void finalizer(obj, client_data) 633 char * obj; 634 char * client_data; 635# endif 636{ 637 tn * t = (tn *)obj; 638 639# ifdef PCR 640 PCR_ThCrSec_EnterSys(); 641# endif 642# if defined(GC_PTHREADS) 643 static pthread_mutex_t incr_lock = PTHREAD_MUTEX_INITIALIZER; 644 pthread_mutex_lock(&incr_lock); 645# elif defined(GC_WIN32_THREADS) 646 EnterCriticalSection(&incr_cs); 647# endif 648 if ((int)(GC_word)client_data != t -> level) { 649 (void)GC_printf("Wrong finalization data - collector is broken\n"); 650 FAIL; 651 } 652 finalized_count++; 653 t -> level = -1; /* detect duplicate finalization immediately */ 654# ifdef PCR 655 PCR_ThCrSec_ExitSys(); 656# endif 657# if defined(GC_PTHREADS) 658 pthread_mutex_unlock(&incr_lock); 659# elif defined(GC_WIN32_THREADS) 660 LeaveCriticalSection(&incr_cs); 661# endif 662} 663 664size_t counter = 0; 665 666# define MAX_FINALIZED 8000 667 668# if !defined(MACOS) 669 GC_FAR GC_word live_indicators[MAX_FINALIZED] = {0}; 670#else 671 /* Too big for THINK_C. have to allocate it dynamically. */ 672 GC_word *live_indicators = 0; 673#endif 674 675int live_indicators_count = 0; 676 677tn * mktree(int n) 678{ 679 tn * result = (tn *)GC_MALLOC(sizeof(tn)); 680 681 collectable_count++; 682# if defined(MACOS) 683 /* get around static data limitations. */ 684 if (!live_indicators) 685 live_indicators = 686 (GC_word*)NewPtrClear(MAX_FINALIZED * sizeof(GC_word)); 687 if (!live_indicators) { 688 (void)GC_printf("Out of memory\n"); 689 exit(1); 690 } 691# endif 692 if (n == 0) return(0); 693 if (result == 0) { 694 (void)GC_printf("Out of memory\n"); 695 exit(1); 696 } 697 result -> level = n; 698 result -> lchild = mktree(n-1); 699 result -> rchild = mktree(n-1); 700 if (counter++ % 17 == 0 && n >= 2) { 701 tn * tmp = result -> lchild -> rchild; 702 703 result -> lchild -> rchild = result -> rchild -> lchild; 704 result -> rchild -> lchild = tmp; 705 } 706 if (counter++ % 119 == 0) { 707 int my_index; 708 709 { 710# ifdef PCR 711 PCR_ThCrSec_EnterSys(); 712# endif 713# if defined(GC_PTHREADS) 714 static pthread_mutex_t incr_lock = PTHREAD_MUTEX_INITIALIZER; 715 pthread_mutex_lock(&incr_lock); 716# elif defined(GC_WIN32_THREADS) 717 EnterCriticalSection(&incr_cs); 718# endif 719 /* Losing a count here causes erroneous report of failure. */ 720 finalizable_count++; 721 my_index = live_indicators_count++; 722# ifdef PCR 723 PCR_ThCrSec_ExitSys(); 724# endif 725# if defined(GC_PTHREADS) 726 pthread_mutex_unlock(&incr_lock); 727# elif defined(GC_WIN32_THREADS) 728 LeaveCriticalSection(&incr_cs); 729# endif 730 } 731 732 GC_REGISTER_FINALIZER((void *)result, finalizer, (void *)(GC_word)n, 733 (GC_finalization_proc *)0, (void * *)0); 734 if (my_index >= MAX_FINALIZED) { 735 GC_printf("live_indicators overflowed\n"); 736 FAIL; 737 } 738 live_indicators[my_index] = 13; 739 if (GC_GENERAL_REGISTER_DISAPPEARING_LINK( 740 (void * *)(&(live_indicators[my_index])), 741 (void *)result) != 0) { 742 GC_printf("GC_general_register_disappearing_link failed\n"); 743 FAIL; 744 } 745 if (GC_unregister_disappearing_link( 746 (void * *) 747 (&(live_indicators[my_index]))) == 0) { 748 GC_printf("GC_unregister_disappearing_link failed\n"); 749 FAIL; 750 } 751 if (GC_GENERAL_REGISTER_DISAPPEARING_LINK( 752 (void * *)(&(live_indicators[my_index])), 753 (void *)result) != 0) { 754 GC_printf("GC_general_register_disappearing_link failed 2\n"); 755 FAIL; 756 } 757 GC_reachable_here(result); 758 } 759 return(result); 760} 761 762void chktree(tn *t, int n) 763{ 764 if (n == 0 && t != 0) { 765 (void)GC_printf("Clobbered a leaf - collector is broken\n"); 766 FAIL; 767 } 768 if (n == 0) return; 769 if (t -> level != n) { 770 (void)GC_printf("Lost a node at level %d - collector is broken\n", n); 771 FAIL; 772 } 773 if (counter++ % 373 == 0) { 774 collectable_count++; 775 (void) GC_MALLOC(counter%5001); 776 } 777 chktree(t -> lchild, n-1); 778 if (counter++ % 73 == 0) { 779 collectable_count++; 780 (void) GC_MALLOC(counter%373); 781 } 782 chktree(t -> rchild, n-1); 783} 784 785 786#if defined(GC_PTHREADS) 787pthread_key_t fl_key; 788 789void * alloc8bytes() 790{ 791# if defined(SMALL_CONFIG) || defined(GC_DEBUG) 792 collectable_count++; 793 return(GC_MALLOC(8)); 794# else 795 void ** my_free_list_ptr; 796 void * my_free_list; 797 798 my_free_list_ptr = (void **)pthread_getspecific(fl_key); 799 if (my_free_list_ptr == 0) { 800 uncollectable_count++; 801 my_free_list_ptr = GC_NEW_UNCOLLECTABLE(void *); 802 if (pthread_setspecific(fl_key, my_free_list_ptr) != 0) { 803 (void)GC_printf("pthread_setspecific failed\n"); 804 FAIL; 805 } 806 } 807 my_free_list = *my_free_list_ptr; 808 if (my_free_list == 0) { 809 my_free_list = GC_malloc_many(8); 810 if (my_free_list == 0) { 811 (void)GC_printf("alloc8bytes out of memory\n"); 812 FAIL; 813 } 814 } 815 *my_free_list_ptr = GC_NEXT(my_free_list); 816 GC_NEXT(my_free_list) = 0; 817 collectable_count++; 818 return(my_free_list); 819# endif 820} 821 822#else 823# define alloc8bytes() GC_MALLOC_ATOMIC(8) 824#endif 825 826void alloc_small(int n) 827{ 828 int i; 829 830 for (i = 0; i < n; i += 8) { 831 atomic_count++; 832 if (alloc8bytes() == 0) { 833 (void)GC_printf("Out of memory\n"); 834 FAIL; 835 } 836 } 837} 838 839# if defined(THREADS) && defined(GC_DEBUG) 840# ifdef VERY_SMALL_CONFIG 841# define TREE_HEIGHT 12 842# else 843# define TREE_HEIGHT 15 844# endif 845# else 846# ifdef VERY_SMALL_CONFIG 847# define TREE_HEIGHT 13 848# else 849# define TREE_HEIGHT 16 850# endif 851# endif 852void tree_test() 853{ 854 tn * root; 855 int i; 856 857 root = mktree(TREE_HEIGHT); 858# ifndef VERY_SMALL_CONFIG 859 alloc_small(5000000); 860# endif 861 chktree(root, TREE_HEIGHT); 862 if (finalized_count && ! dropped_something) { 863 (void)GC_printf("Premature finalization - collector is broken\n"); 864 FAIL; 865 } 866 dropped_something = 1; 867 GC_noop(root); /* Root needs to remain live until */ 868 /* dropped_something is set. */ 869 root = mktree(TREE_HEIGHT); 870 chktree(root, TREE_HEIGHT); 871 for (i = TREE_HEIGHT; i >= 0; i--) { 872 root = mktree(i); 873 chktree(root, i); 874 } 875# ifndef VERY_SMALL_CONFIG 876 alloc_small(5000000); 877# endif 878} 879 880unsigned n_tests = 0; 881 882GC_word bm_huge[10] = { 883 0xffffffff, 884 0xffffffff, 885 0xffffffff, 886 0xffffffff, 887 0xffffffff, 888 0xffffffff, 889 0xffffffff, 890 0xffffffff, 891 0xffffffff, 892 0x00ffffff, 893}; 894 895/* A very simple test of explicitly typed allocation */ 896void typed_test() 897{ 898 GC_word * old, * new; 899 GC_word bm3 = 0x3; 900 GC_word bm2 = 0x2; 901 GC_word bm_large = 0xf7ff7fff; 902 GC_descr d1 = GC_make_descriptor(&bm3, 2); 903 GC_descr d2 = GC_make_descriptor(&bm2, 2); 904 GC_descr d3 = GC_make_descriptor(&bm_large, 32); 905 GC_descr d4 = GC_make_descriptor(bm_huge, 320); 906 GC_word * x = (GC_word *)GC_malloc_explicitly_typed(2000, d4); 907 int i; 908 909# ifndef LINT 910 (void)GC_make_descriptor(&bm_large, 32); 911# endif 912 collectable_count++; 913 old = 0; 914 for (i = 0; i < 4000; i++) { 915 collectable_count++; 916 new = (GC_word *) GC_malloc_explicitly_typed(4 * sizeof(GC_word), d1); 917 if (0 != new[0] || 0 != new[1]) { 918 GC_printf("Bad initialization by GC_malloc_explicitly_typed\n"); 919 FAIL; 920 } 921 new[0] = 17; 922 new[1] = (GC_word)old; 923 old = new; 924 collectable_count++; 925 new = (GC_word *) GC_malloc_explicitly_typed(4 * sizeof(GC_word), d2); 926 new[0] = 17; 927 new[1] = (GC_word)old; 928 old = new; 929 collectable_count++; 930 new = (GC_word *) GC_malloc_explicitly_typed(33 * sizeof(GC_word), d3); 931 new[0] = 17; 932 new[1] = (GC_word)old; 933 old = new; 934 collectable_count++; 935 new = (GC_word *) GC_calloc_explicitly_typed(4, 2 * sizeof(GC_word), 936 d1); 937 new[0] = 17; 938 new[1] = (GC_word)old; 939 old = new; 940 collectable_count++; 941 if (i & 0xff) { 942 new = (GC_word *) GC_calloc_explicitly_typed(7, 3 * sizeof(GC_word), 943 d2); 944 } else { 945 new = (GC_word *) GC_calloc_explicitly_typed(1001, 946 3 * sizeof(GC_word), 947 d2); 948 if (0 != new[0] || 0 != new[1]) { 949 GC_printf("Bad initialization by GC_malloc_explicitly_typed\n"); 950 FAIL; 951 } 952 } 953 new[0] = 17; 954 new[1] = (GC_word)old; 955 old = new; 956 } 957 for (i = 0; i < 20000; i++) { 958 if (new[0] != 17) { 959 (void)GC_printf("typed alloc failed at %lu\n", 960 (unsigned long)i); 961 FAIL; 962 } 963 new[0] = 0; 964 old = new; 965 new = (GC_word *)(old[1]); 966 } 967 GC_gcollect(); 968 GC_noop(x); 969} 970 971int fail_count = 0; 972 973#ifndef __STDC__ 974/*ARGSUSED*/ 975void fail_proc1(x) 976void * x; 977{ 978 fail_count++; 979} 980 981#else 982 983/*ARGSUSED*/ 984void fail_proc1(void * x) 985{ 986 fail_count++; 987} 988 989static void uniq(void *p, ...) { 990 va_list a; 991 void *q[100]; 992 int n = 0, i, j; 993 q[n++] = p; 994 va_start(a,p); 995 for (;(q[n] = va_arg(a,void *));n++) ; 996 va_end(a); 997 for (i=0; i<n; i++) 998 for (j=0; j<i; j++) 999 if (q[i] == q[j]) { 1000 GC_printf( 1001 "Apparently failed to mark from some function arguments.\n" 1002 "Perhaps GC_push_regs was configured incorrectly?\n" 1003 ); 1004 FAIL; 1005 } 1006} 1007 1008#endif /* __STDC__ */ 1009 1010#ifdef THREADS 1011# define TEST_FAIL_COUNT(n) 1 1012#else 1013# define TEST_FAIL_COUNT(n) (fail_count >= (n)) 1014#endif 1015 1016void run_one_test() 1017{ 1018 char *x; 1019# ifdef LINT 1020 char *y = 0; 1021# else 1022 char *y = (char *)(size_t)fail_proc1; 1023# endif 1024 DCL_LOCK_STATE; 1025 1026# ifdef FIND_LEAK 1027 (void)GC_printf( 1028 "This test program is not designed for leak detection mode\n"); 1029 (void)GC_printf("Expect lots of problems.\n"); 1030# endif 1031 GC_FREE(0); 1032# ifndef DBG_HDRS_ALL 1033 collectable_count += 3; 1034 if ((GC_size(GC_malloc(7)) != 8 && 1035 GC_size(GC_malloc(7)) != MIN_WORDS * sizeof(GC_word)) 1036 || GC_size(GC_malloc(15)) != 16) { 1037 (void)GC_printf("GC_size produced unexpected results\n"); 1038 FAIL; 1039 } 1040 collectable_count += 1; 1041 if (GC_size(GC_malloc(0)) != MIN_WORDS * sizeof(GC_word)) { 1042 (void)GC_printf("GC_malloc(0) failed: GC_size returns %ld\n", 1043 (unsigned long)GC_size(GC_malloc(0))); 1044 FAIL; 1045 } 1046 collectable_count += 1; 1047 if (GC_size(GC_malloc_uncollectable(0)) != MIN_WORDS * sizeof(GC_word)) { 1048 (void)GC_printf("GC_malloc_uncollectable(0) failed\n"); 1049 FAIL; 1050 } 1051 GC_is_valid_displacement_print_proc = fail_proc1; 1052 GC_is_visible_print_proc = fail_proc1; 1053 collectable_count += 1; 1054 x = GC_malloc(16); 1055 if (GC_base(x + 13) != x) { 1056 (void)GC_printf("GC_base(heap ptr) produced incorrect result\n"); 1057 FAIL; 1058 } 1059# ifndef PCR 1060 if (GC_base(y) != 0) { 1061 (void)GC_printf("GC_base(fn_ptr) produced incorrect result\n"); 1062 FAIL; 1063 } 1064# endif 1065 if (GC_same_obj(x+5, x) != x + 5) { 1066 (void)GC_printf("GC_same_obj produced incorrect result\n"); 1067 FAIL; 1068 } 1069 if (GC_is_visible(y) != y || GC_is_visible(x) != x) { 1070 (void)GC_printf("GC_is_visible produced incorrect result\n"); 1071 FAIL; 1072 } 1073 if (!TEST_FAIL_COUNT(1)) { 1074# if!(defined(RS6000) || defined(POWERPC) || defined(IA64)) || defined(M68K) 1075 /* ON RS6000s function pointers point to a descriptor in the */ 1076 /* data segment, so there should have been no failures. */ 1077 /* The same applies to IA64. Something similar seems to */ 1078 /* be going on with NetBSD/M68K. */ 1079 (void)GC_printf("GC_is_visible produced wrong failure indication\n"); 1080 FAIL; 1081# endif 1082 } 1083 if (GC_is_valid_displacement(y) != y 1084 || GC_is_valid_displacement(x) != x 1085 || GC_is_valid_displacement(x + 3) != x + 3) { 1086 (void)GC_printf( 1087 "GC_is_valid_displacement produced incorrect result\n"); 1088 FAIL; 1089 } 1090# if defined(__STDC__) && !defined(MSWIN32) && !defined(MSWINCE) 1091 /* Harder to test under Windows without a gc.h declaration. */ 1092 { 1093 size_t i; 1094 extern void *GC_memalign(); 1095 1096 GC_malloc(17); 1097 for (i = sizeof(GC_word); i < 512; i *= 2) { 1098 GC_word result = (GC_word) GC_memalign(i, 17); 1099 if (result % i != 0 || result == 0 || *(int *)result != 0) FAIL; 1100 } 1101 } 1102# endif 1103# ifndef ALL_INTERIOR_POINTERS 1104# if defined(RS6000) || defined(POWERPC) 1105 if (!TEST_FAIL_COUNT(1)) { 1106# else 1107 if (GC_all_interior_pointers && !TEST_FAIL_COUNT(1) 1108 || !GC_all_interior_pointers && !TEST_FAIL_COUNT(2)) { 1109# endif 1110 (void)GC_printf("GC_is_valid_displacement produced wrong failure indication\n"); 1111 FAIL; 1112 } 1113# endif 1114# endif /* DBG_HDRS_ALL */ 1115 /* Test floating point alignment */ 1116 collectable_count += 2; 1117 *(double *)GC_MALLOC(sizeof(double)) = 1.0; 1118 *(double *)GC_MALLOC(sizeof(double)) = 1.0; 1119# ifdef GC_GCJ_SUPPORT 1120 GC_REGISTER_DISPLACEMENT(sizeof(struct fake_vtable *)); 1121 GC_init_gcj_malloc(0, (void *)fake_gcj_mark_proc); 1122# endif 1123 /* Make sure that fn arguments are visible to the collector. */ 1124# ifdef __STDC__ 1125 uniq( 1126 GC_malloc(12), GC_malloc(12), GC_malloc(12), 1127 (GC_gcollect(),GC_malloc(12)), 1128 GC_malloc(12), GC_malloc(12), GC_malloc(12), 1129 (GC_gcollect(),GC_malloc(12)), 1130 GC_malloc(12), GC_malloc(12), GC_malloc(12), 1131 (GC_gcollect(),GC_malloc(12)), 1132 GC_malloc(12), GC_malloc(12), GC_malloc(12), 1133 (GC_gcollect(),GC_malloc(12)), 1134 GC_malloc(12), GC_malloc(12), GC_malloc(12), 1135 (GC_gcollect(),GC_malloc(12)), 1136 (void *)0); 1137# endif 1138 /* GC_malloc(0) must return NULL or something we can deallocate. */ 1139 GC_free(GC_malloc(0)); 1140 GC_free(GC_malloc_atomic(0)); 1141 GC_free(GC_malloc(0)); 1142 GC_free(GC_malloc_atomic(0)); 1143 /* Repeated list reversal test. */ 1144 reverse_test(); 1145# ifdef PRINTSTATS 1146 GC_printf("-------------Finished reverse_test\n"); 1147# endif 1148# ifndef DBG_HDRS_ALL 1149 typed_test(); 1150# ifdef PRINTSTATS 1151 GC_printf("-------------Finished typed_test\n"); 1152# endif 1153# endif /* DBG_HDRS_ALL */ 1154 tree_test(); 1155 LOCK(); 1156 n_tests++; 1157 UNLOCK(); 1158# if defined(THREADS) && defined(HANDLE_FORK) 1159 if (fork() == 0) { 1160 GC_gcollect(); 1161 tiny_reverse_test(0); 1162 GC_gcollect(); 1163 GC_printf("Finished a child process\n"); 1164 exit(0); 1165 } 1166# endif 1167 /* GC_printf("Finished %x\n", pthread_self()); */ 1168} 1169 1170void check_heap_stats() 1171{ 1172 size_t max_heap_sz; 1173 int i; 1174 int still_live; 1175 int late_finalize_count = 0; 1176 1177# ifdef VERY_SMALL_CONFIG 1178 /* these are something of a guess */ 1179 if (sizeof(char *) > 4) { 1180 max_heap_sz = 4500000; 1181 } else { 1182 max_heap_sz = 2800000; 1183 } 1184# else 1185 if (sizeof(char *) > 4) { 1186 max_heap_sz = 19000000; 1187 } else { 1188 max_heap_sz = 11000000; 1189 } 1190# endif 1191# ifdef GC_DEBUG 1192 max_heap_sz *= 2; 1193# ifdef SAVE_CALL_CHAIN 1194 max_heap_sz *= 3; 1195# ifdef SAVE_CALL_COUNT 1196 max_heap_sz += max_heap_sz * SAVE_CALL_COUNT/4; 1197# endif 1198# endif 1199# endif 1200 /* Garbage collect repeatedly so that all inaccessible objects */ 1201 /* can be finalized. */ 1202 while (GC_collect_a_little()) { } 1203 for (i = 0; i < 16; i++) { 1204 GC_gcollect(); 1205 late_finalize_count += GC_invoke_finalizers(); 1206 } 1207 (void)GC_printf("Completed %u tests\n", n_tests); 1208 (void)GC_printf("Allocated %d collectable objects\n", collectable_count); 1209 (void)GC_printf("Allocated %d uncollectable objects\n", 1210 uncollectable_count); 1211 (void)GC_printf("Allocated %d atomic objects\n", atomic_count); 1212 (void)GC_printf("Allocated %d stubborn objects\n", stubborn_count); 1213 (void)GC_printf("Finalized %d/%d objects - ", 1214 finalized_count, finalizable_count); 1215# ifdef FINALIZE_ON_DEMAND 1216 if (finalized_count != late_finalize_count) { 1217 (void)GC_printf("Demand finalization error\n"); 1218 FAIL; 1219 } 1220# endif 1221 if (finalized_count > finalizable_count 1222 || finalized_count < finalizable_count/2) { 1223 (void)GC_printf("finalization is probably broken\n"); 1224 FAIL; 1225 } else { 1226 (void)GC_printf("finalization is probably ok\n"); 1227 } 1228 still_live = 0; 1229 for (i = 0; i < MAX_FINALIZED; i++) { 1230 if (live_indicators[i] != 0) { 1231 still_live++; 1232 } 1233 } 1234 i = finalizable_count - finalized_count - still_live; 1235 if (0 != i) { 1236 GC_printf("%d disappearing links remain and %d more objects " 1237 "were not finalized\n", still_live, i); 1238 if (i > 10) { 1239 GC_printf("\tVery suspicious!\n"); 1240 } else { 1241 GC_printf("\tSlightly suspicious, but probably OK.\n"); 1242 } 1243 } 1244 (void)GC_printf("Total number of bytes allocated is %lu\n", 1245 (unsigned long) 1246 (GC_bytes_allocd + GC_bytes_allocd_before_gc)); 1247 (void)GC_printf("Final heap size is %lu bytes\n", 1248 (unsigned long)GC_get_heap_size()); 1249 if (GC_bytes_allocd + GC_bytes_allocd_before_gc 1250# ifdef VERY_SMALL_CONFIG 1251 < 2700000*n_tests) { 1252# else 1253 < 33500000*n_tests) { 1254# endif 1255 (void)GC_printf("Incorrect execution - missed some allocations\n"); 1256 FAIL; 1257 } 1258 if (GC_get_heap_size() > max_heap_sz*n_tests) { 1259 (void)GC_printf("Unexpected heap growth - collector may be broken\n"); 1260 FAIL; 1261 } 1262 (void)GC_printf("Collector appears to work\n"); 1263} 1264 1265#if defined(MACOS) 1266void SetMinimumStack(long minSize) 1267{ 1268 long newApplLimit; 1269 1270 if (minSize > LMGetDefltStack()) 1271 { 1272 newApplLimit = (long) GetApplLimit() 1273 - (minSize - LMGetDefltStack()); 1274 SetApplLimit((Ptr) newApplLimit); 1275 MaxApplZone(); 1276 } 1277} 1278 1279#define cMinStackSpace (512L * 1024L) 1280 1281#endif 1282 1283#ifdef __STDC__ 1284 void warn_proc(char *msg, GC_word p) 1285#else 1286 void warn_proc(msg, p) 1287 char *msg; 1288 GC_word p; 1289#endif 1290{ 1291 GC_printf(msg, (unsigned long)p); 1292 /*FAIL;*/ 1293} 1294 1295 1296#if !defined(PCR) \ 1297 && !defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS) \ 1298 || defined(LINT) 1299#if defined(MSWIN32) && !defined(__MINGW32__) 1300 int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prev, LPTSTR cmd, int n) 1301#else 1302 int main() 1303#endif 1304{ 1305# if defined(DJGPP) 1306 int dummy; 1307# endif 1308 n_tests = 0; 1309 1310# if defined(DJGPP) 1311 /* No good way to determine stack base from library; do it */ 1312 /* manually on this platform. */ 1313 GC_stackbottom = (void *)(&dummy); 1314# endif 1315# if defined(MACOS) 1316 /* Make sure we have lots and lots of stack space. */ 1317 SetMinimumStack(cMinStackSpace); 1318 /* Cheat and let stdio initialize toolbox for us. */ 1319 printf("Testing GC Macintosh port.\n"); 1320# endif 1321 GC_INIT(); /* Only needed on a few platforms. */ 1322 (void) GC_set_warn_proc(warn_proc); 1323# if (defined(MPROTECT_VDB) || defined(PROC_VDB) || defined(GWW_VDB)) \ 1324 && !defined(MAKE_BACK_GRAPH) && !defined(NO_INCREMENTAL) 1325 GC_enable_incremental(); 1326 (void) GC_printf("Switched to incremental mode\n"); 1327# if defined(MPROTECT_VDB) 1328 (void)GC_printf("Emulating dirty bits with mprotect/signals\n"); 1329# else 1330# ifdef PROC_VDB 1331 (void)GC_printf("Reading dirty bits from /proc\n"); 1332# else 1333 (void)GC_printf("Using DEFAULT_VDB dirty bit implementation\n"); 1334# endif 1335# endif 1336# endif 1337 run_one_test(); 1338 check_heap_stats(); 1339# ifndef MSWINCE 1340 (void)fflush(stdout); 1341# endif 1342# ifdef LINT 1343 /* Entry points we should be testing, but aren't. */ 1344 /* Some can be tested by defining GC_DEBUG at the top of this file */ 1345 /* This is a bit SunOS4 specific. */ 1346 GC_noop(GC_expand_hp, GC_add_roots, GC_clear_roots, 1347 GC_register_disappearing_link, 1348 GC_register_finalizer_ignore_self, 1349 GC_debug_register_displacement, 1350 GC_print_obj, GC_debug_change_stubborn, 1351 GC_debug_end_stubborn_change, GC_debug_malloc_uncollectable, 1352 GC_debug_free, GC_debug_realloc, GC_generic_malloc_words_small, 1353 GC_init, GC_make_closure, GC_debug_invoke_finalizer, 1354 GC_page_was_ever_dirty, GC_is_fresh, 1355 GC_malloc_ignore_off_page, GC_malloc_atomic_ignore_off_page, 1356 GC_set_max_heap_size, GC_get_bytes_since_gc, 1357 GC_get_total_bytes, GC_pre_incr, GC_post_incr); 1358# endif 1359# ifdef MSWIN32 1360 GC_win32_free_heap(); 1361# endif 1362 return(0); 1363} 1364# endif 1365 1366#if defined(GC_WIN32_THREADS) && !defined(GC_PTHREADS) 1367 1368DWORD __stdcall thr_run_one_test(void *arg) 1369{ 1370 run_one_test(); 1371 return 0; 1372} 1373 1374#ifdef MSWINCE 1375HANDLE win_created_h; 1376HWND win_handle; 1377 1378LRESULT CALLBACK window_proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 1379{ 1380 LRESULT ret = 0; 1381 switch (uMsg) { 1382 case WM_HIBERNATE: 1383 GC_printf("Received WM_HIBERNATE, calling GC_gcollect\n"); 1384 GC_gcollect(); 1385 break; 1386 case WM_CLOSE: 1387 GC_printf("Received WM_CLOSE, closing window\n"); 1388 DestroyWindow(hwnd); 1389 break; 1390 case WM_DESTROY: 1391 PostQuitMessage(0); 1392 break; 1393 default: 1394 ret = DefWindowProc(hwnd, uMsg, wParam, lParam); 1395 break; 1396 } 1397 return ret; 1398} 1399 1400DWORD __stdcall thr_window(void *arg) 1401{ 1402 WNDCLASS win_class = { 1403 CS_NOCLOSE, 1404 window_proc, 1405 0, 1406 0, 1407 GetModuleHandle(NULL), 1408 NULL, 1409 NULL, 1410 (HBRUSH)(COLOR_APPWORKSPACE+1), 1411 NULL, 1412 L"GCtestWindow" 1413 }; 1414 MSG msg; 1415 1416 if (!RegisterClass(&win_class)) 1417 FAIL; 1418 1419 win_handle = CreateWindowEx( 1420 0, 1421 L"GCtestWindow", 1422 L"GCtest", 1423 0, 1424 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 1425 NULL, 1426 NULL, 1427 GetModuleHandle(NULL), 1428 NULL); 1429 1430 if (win_handle == NULL) 1431 FAIL; 1432 1433 SetEvent(win_created_h); 1434 1435 ShowWindow(win_handle, SW_SHOW); 1436 UpdateWindow(win_handle); 1437 1438 while (GetMessage(&msg, NULL, 0, 0)) { 1439 TranslateMessage(&msg); 1440 DispatchMessage(&msg); 1441 } 1442 1443 return 0; 1444} 1445#endif 1446 1447#define NTEST 2 1448 1449# ifdef MSWINCE 1450int APIENTRY GC_WinMain(HINSTANCE instance, HINSTANCE prev, LPWSTR cmd, int n) 1451# else 1452int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prev, LPSTR cmd, int n) 1453# endif 1454{ 1455# if NTEST > 0 1456 HANDLE h[NTEST]; 1457 int i; 1458# endif 1459# ifdef MSWINCE 1460 HANDLE win_thr_h; 1461# endif 1462 DWORD thread_id; 1463 1464# ifdef GC_DLL 1465 GC_use_DllMain(); /* Test with implicit thread registration if possible. */ 1466 GC_printf("Using DllMain to track threads\n"); 1467# endif 1468 GC_INIT(); 1469# ifndef NO_INCREMENTAL 1470 GC_enable_incremental(); 1471# endif 1472 InitializeCriticalSection(&incr_cs); 1473 (void) GC_set_warn_proc(warn_proc); 1474# ifdef MSWINCE 1475 win_created_h = CreateEvent(NULL, FALSE, FALSE, NULL); 1476 if (win_created_h == (HANDLE)NULL) { 1477 (void)GC_printf("Event creation failed %\n", GetLastError()); 1478 FAIL; 1479 } 1480 win_thr_h = GC_CreateThread(NULL, 0, thr_window, 0, 0, &thread_id); 1481 if (win_thr_h == (HANDLE)NULL) { 1482 (void)GC_printf("Thread creation failed %d\n", GetLastError()); 1483 FAIL; 1484 }