Synopsis - Cross-Reference

File: /src/Synopsis/gc/tests/test.c
   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.	*/
  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    }</