Blame tests/test_cpp.cc

Packit d28291
/****************************************************************************
Packit d28291
Copyright (c) 1994 by Xerox Corporation.  All rights reserved.
Packit d28291
Packit d28291
THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
Packit d28291
OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
Packit d28291
Packit d28291
Permission is hereby granted to use or copy this program for any
Packit d28291
purpose, provided the above notices are retained on all copies.
Packit d28291
Permission to modify the code and to distribute modified code is
Packit d28291
granted, provided the above notices are retained, and a notice that
Packit d28291
the code was modified is included with the above copyright notice.
Packit d28291
****************************************************************************
Packit d28291
Packit d28291
usage: test_cpp number-of-iterations
Packit d28291
Packit d28291
This program tries to test the specific C++ functionality provided by
Packit d28291
gc_c++.h that isn't tested by the more general test routines of the
Packit d28291
collector.
Packit d28291
Packit d28291
A recommended value for number-of-iterations is 10, which will take a
Packit d28291
few minutes to complete.
Packit d28291
Packit d28291
***************************************************************************/
Packit d28291
Packit d28291
#ifdef HAVE_CONFIG_H
Packit d28291
# include "config.h"
Packit d28291
#endif
Packit d28291
Packit d28291
#undef GC_BUILD
Packit d28291
Packit d28291
#include "gc_cpp.h"
Packit d28291
Packit d28291
#include <stdio.h>
Packit d28291
#include <stdlib.h>
Packit d28291
#include <string.h>
Packit d28291
Packit d28291
#ifndef DONT_USE_STD_ALLOCATOR
Packit d28291
# include "gc_allocator.h"
Packit d28291
#else
Packit d28291
  /* Note: This works only for ancient STL versions.    */
Packit d28291
# include "new_gc_alloc.h"
Packit d28291
#endif
Packit d28291
Packit d28291
extern "C" {
Packit d28291
# include "private/gcconfig.h"
Packit d28291
Packit d28291
# ifndef GC_API_PRIV
Packit d28291
#   define GC_API_PRIV GC_API
Packit d28291
# endif
Packit d28291
  GC_API_PRIV void GC_printf(const char * format, ...);
Packit d28291
  /* Use GC private output to reach the same log file.  */
Packit d28291
  /* Don't include gc_priv.h, since that may include Windows system     */
Packit d28291
  /* header files that don't take kindly to this context.               */
Packit d28291
}
Packit d28291
Packit d28291
#ifdef MSWIN32
Packit d28291
# include <windows.h>
Packit d28291
#endif
Packit d28291
Packit d28291
#ifdef GC_NAME_CONFLICT
Packit d28291
# define USE_GC GC_NS_QUALIFY(UseGC)
Packit d28291
  struct foo * GC;
Packit d28291
#else
Packit d28291
# define USE_GC GC_NS_QUALIFY(GC)
Packit d28291
#endif
Packit d28291
Packit d28291
#define my_assert( e ) \
Packit d28291
    if (! (e)) { \
Packit d28291
        GC_printf( "Assertion failure in " __FILE__ ", line %d: " #e "\n", \
Packit d28291
                    __LINE__ ); \
Packit d28291
        exit( 1 ); }
Packit d28291
Packit d28291
#ifndef GC_ATTR_EXPLICIT
Packit d28291
# if (__cplusplus >= 201103L) || defined(CPPCHECK)
Packit d28291
#   define GC_ATTR_EXPLICIT explicit
Packit d28291
# else
Packit d28291
#   define GC_ATTR_EXPLICIT /* empty */
Packit d28291
# endif
Packit d28291
#endif
Packit d28291
Packit d28291
class A {public:
Packit d28291
    /* An uncollectible class. */
Packit d28291
Packit d28291
    GC_ATTR_EXPLICIT A( int iArg ): i( iArg ) {}
Packit d28291
    void Test( int iArg ) {
Packit d28291
        my_assert( i == iArg );}
Packit d28291
    int i;};
Packit d28291
Packit d28291
Packit d28291
class B: public GC_NS_QUALIFY(gc), public A { public:
Packit d28291
    /* A collectible class. */
Packit d28291
Packit d28291
    GC_ATTR_EXPLICIT B( int j ): A( j ) {}
Packit d28291
    virtual ~B() {
Packit d28291
        my_assert( deleting );}
Packit d28291
    static void Deleting( int on ) {
Packit d28291
        deleting = on;}
Packit d28291
    static int deleting;};
Packit d28291
Packit d28291
int B::deleting = 0;
Packit d28291
Packit d28291
Packit d28291
class C: public GC_NS_QUALIFY(gc_cleanup), public A { public:
Packit d28291
    /* A collectible class with cleanup and virtual multiple inheritance. */
Packit d28291
Packit d28291
    GC_ATTR_EXPLICIT C( int levelArg ): A( levelArg ), level( levelArg ) {
Packit d28291
        nAllocated++;
Packit d28291
        if (level > 0) {
Packit d28291
            left = new C( level - 1 );
Packit d28291
            right = new C( level - 1 );}
Packit d28291
        else {
Packit d28291
            left = right = 0;}}
Packit d28291
    ~C() {
Packit d28291
        this->A::Test( level );
Packit d28291
        nFreed++;
Packit d28291
        my_assert( level == 0 ?
Packit d28291
                   left == 0 && right == 0 :
Packit d28291
                   level == left->level + 1 && level == right->level + 1 );
Packit d28291
        left = right = 0;
Packit d28291
        level = -123456;}
Packit d28291
    static void Test() {
Packit d28291
        my_assert( nFreed <= nAllocated && nFreed >= .8 * nAllocated );}
Packit d28291
Packit d28291
    static int nFreed;
Packit d28291
    static int nAllocated;
Packit d28291
    int level;
Packit d28291
    C* left;
Packit d28291
    C* right;};
Packit d28291
Packit d28291
int C::nFreed = 0;
Packit d28291
int C::nAllocated = 0;
Packit d28291
Packit d28291
Packit d28291
class D: public GC_NS_QUALIFY(gc) { public:
Packit d28291
    /* A collectible class with a static member function to be used as
Packit d28291
    an explicit clean-up function supplied to ::new. */
Packit d28291
Packit d28291
    GC_ATTR_EXPLICIT D( int iArg ): i( iArg ) {
Packit d28291
        nAllocated++;}
Packit d28291
    static void CleanUp( void* obj, void* data ) {
Packit d28291
        D* self = static_cast<D*>(obj);
Packit d28291
        nFreed++;
Packit d28291
        my_assert( (GC_word)self->i == (GC_word)data );}
Packit d28291
    static void Test() {
Packit d28291
        my_assert( nFreed >= .8 * nAllocated );}
Packit d28291
Packit d28291
    int i;
Packit d28291
    static int nFreed;
Packit d28291
    static int nAllocated;};
Packit d28291
Packit d28291
int D::nFreed = 0;
Packit d28291
int D::nAllocated = 0;
Packit d28291
Packit d28291
Packit d28291
class E: public GC_NS_QUALIFY(gc_cleanup) { public:
Packit d28291
    /* A collectible class with clean-up for use by F. */
Packit d28291
Packit d28291
    E() {
Packit d28291
        nAllocated++;}
Packit d28291
    ~E() {
Packit d28291
        nFreed++;}
Packit d28291
Packit d28291
    static int nFreed;
Packit d28291
    static int nAllocated;};
Packit d28291
Packit d28291
int E::nFreed = 0;
Packit d28291
int E::nAllocated = 0;
Packit d28291
Packit d28291
Packit d28291
class F: public E {public:
Packit d28291
    /* A collectible class with clean-up, a base with clean-up, and a
Packit d28291
    member with clean-up. */
Packit d28291
Packit d28291
    F() {
Packit d28291
        nAllocatedF++;
Packit d28291
    }
Packit d28291
Packit d28291
    ~F() {
Packit d28291
        nFreedF++;
Packit d28291
    }
Packit d28291
Packit d28291
    static void Test() {
Packit d28291
        my_assert(nFreedF >= .8 * nAllocatedF);
Packit d28291
        my_assert(2 * nFreedF == nFreed);
Packit d28291
    }
Packit d28291
Packit d28291
    E e;
Packit d28291
    static int nFreedF;
Packit d28291
    static int nAllocatedF;
Packit d28291
};
Packit d28291
Packit d28291
int F::nFreedF = 0;
Packit d28291
int F::nAllocatedF = 0;
Packit d28291
Packit d28291
Packit d28291
GC_word Disguise( void* p ) {
Packit d28291
    return ~ (GC_word) p;}
Packit d28291
Packit d28291
void* Undisguise( GC_word i ) {
Packit d28291
    return (void*) ~ i;}
Packit d28291
Packit d28291
#if ((defined(MSWIN32) && !defined(__MINGW32__)) || defined(MSWINCE)) \
Packit d28291
    && !defined(NO_WINMAIN_ENTRY)
Packit d28291
  int APIENTRY WinMain( HINSTANCE /* instance */, HINSTANCE /* prev */,
Packit d28291
                       LPSTR cmd, int /* cmdShow */)
Packit d28291
  {
Packit d28291
    int argc = 0;
Packit d28291
    char* argv[ 3 ];
Packit d28291
Packit d28291
#   if defined(CPPCHECK)
Packit d28291
      GC_noop1((GC_word)&WinMain);
Packit d28291
#   endif
Packit d28291
    if (cmd != 0)
Packit d28291
      for (argc = 1; argc < (int)(sizeof(argv) / sizeof(argv[0])); argc++) {
Packit d28291
        // Parse the command-line string.  Non-reentrant strtok() is not used
Packit d28291
        // to avoid complains of static analysis tools.  (And, strtok_r() is
Packit d28291
        // not available on some platforms.)  The code is equivalent to:
Packit d28291
        //   if (!(argv[argc] = strtok(argc == 1 ? cmd : 0, " \t"))) break;
Packit d28291
        if (NULL == cmd) {
Packit d28291
          argv[argc] = NULL;
Packit d28291
          break;
Packit d28291
        }
Packit d28291
        argv[argc] = cmd;
Packit d28291
        for (; *cmd != '\0'; cmd++) {
Packit d28291
          if (*cmd != ' ' && *cmd != '\t')
Packit d28291
            break;
Packit d28291
        }
Packit d28291
        if ('\0' == *cmd) {
Packit d28291
          argv[argc] = NULL;
Packit d28291
          break;
Packit d28291
        }
Packit d28291
        argv[argc] = cmd;
Packit d28291
        while (*(++cmd) != '\0') {
Packit d28291
          if (*cmd == ' ' || *cmd == '\t')
Packit d28291
            break;
Packit d28291
        }
Packit d28291
        if (*cmd != '\0') {
Packit d28291
          *(cmd++) = '\0';
Packit d28291
        } else {
Packit d28291
          cmd = NULL;
Packit d28291
        }
Packit d28291
      }
Packit d28291
#elif defined(MACOS)
Packit d28291
  int main() {
Packit d28291
    char* argv_[] = {"test_cpp", "10"}; // MacOS doesn't have a command line
Packit d28291
    argv = argv_;
Packit d28291
    argc = sizeof(argv_)/sizeof(argv_[0]);
Packit d28291
#else
Packit d28291
  int main( int argc, char* argv[] ) {
Packit d28291
#endif
Packit d28291
Packit d28291
    GC_set_all_interior_pointers(1);
Packit d28291
                        /* needed due to C++ multiple inheritance used  */
Packit d28291
Packit d28291
    GC_INIT();
Packit d28291
Packit d28291
    int i, iters, n;
Packit d28291
#   ifndef DONT_USE_STD_ALLOCATOR
Packit d28291
      int *x = gc_allocator<int>().allocate(1);
Packit d28291
      int *xio;
Packit d28291
      xio = gc_allocator_ignore_off_page<int>().allocate(1);
Packit d28291
      (void)xio;
Packit d28291
      int **xptr = traceable_allocator<int *>().allocate(1);
Packit d28291
#   else
Packit d28291
      int *x = (int *)gc_alloc::allocate(sizeof(int));
Packit d28291
#   endif
Packit d28291
    *x = 29;
Packit d28291
#   ifndef DONT_USE_STD_ALLOCATOR
Packit d28291
      if (!xptr) {
Packit d28291
        fprintf(stderr, "Out of memory!\n");
Packit d28291
        exit(3);
Packit d28291
      }
Packit d28291
      *xptr = x;
Packit d28291
      x = 0;
Packit d28291
#   endif
Packit d28291
    if (argc != 2
Packit d28291
        || (n = (int)COVERT_DATAFLOW(atoi(argv[1]))) <= 0) {
Packit d28291
      GC_printf("usage: test_cpp number-of-iterations\n"
Packit d28291
                "Assuming 10 iters\n");
Packit d28291
      n = 10;
Packit d28291
    }
Packit d28291
Packit d28291
    for (iters = 1; iters <= n; iters++) {
Packit d28291
        GC_printf( "Starting iteration %d\n", iters );
Packit d28291
Packit d28291
            /* Allocate some uncollectible As and disguise their pointers.
Packit d28291
            Later we'll check to see if the objects are still there.  We're
Packit d28291
            checking to make sure these objects really are uncollectible. */
Packit d28291
        GC_word as[ 1000 ];
Packit d28291
        GC_word bs[ 1000 ];
Packit d28291
        for (i = 0; i < 1000; i++) {
Packit d28291
            as[ i ] = Disguise( new (GC_NS_QUALIFY(NoGC)) A(i) );
Packit d28291
            bs[ i ] = Disguise( new (GC_NS_QUALIFY(NoGC)) B(i) ); }
Packit d28291
Packit d28291
            /* Allocate a fair number of finalizable Cs, Ds, and Fs.
Packit d28291
            Later we'll check to make sure they've gone away. */
Packit d28291
        for (i = 0; i < 1000; i++) {
Packit d28291
            C* c = new C( 2 );
Packit d28291
            C c1( 2 );           /* stack allocation should work too */
Packit d28291
            D* d;
Packit d28291
            F* f;
Packit d28291
            d = ::new (USE_GC, D::CleanUp, (void*)(GC_word)i) D( i );
Packit d28291
            (void)d;
Packit d28291
            f = new F;
Packit d28291
            F** fa = new F*[1];
Packit d28291
            fa[0] = f;
Packit d28291
            (void)fa;
Packit d28291
            delete[] fa;
Packit d28291
            if (0 == i % 10) delete c;}
Packit d28291
Packit d28291
            /* Allocate a very large number of collectible As and Bs and
Packit d28291
            drop the references to them immediately, forcing many
Packit d28291
            collections. */
Packit d28291
        for (i = 0; i < 1000000; i++) {
Packit d28291
            A* a;
Packit d28291
            a = new (USE_GC) A( i );
Packit d28291
            (void)a;
Packit d28291
            B* b;
Packit d28291
            b = new B( i );
Packit d28291
            (void)b;
Packit d28291
            b = new (USE_GC) B( i );
Packit d28291
            if (0 == i % 10) {
Packit d28291
                B::Deleting( 1 );
Packit d28291
                delete b;
Packit d28291
                B::Deleting( 0 );}
Packit d28291
#           ifdef FINALIZE_ON_DEMAND
Packit d28291
              GC_invoke_finalizers();
Packit d28291
#           endif
Packit d28291
            }
Packit d28291
Packit d28291
            /* Make sure the uncollectible As and Bs are still there. */
Packit d28291
        for (i = 0; i < 1000; i++) {
Packit d28291
            A* a = static_cast<A*>(Undisguise(as[i]));
Packit d28291
            B* b = static_cast<B*>(Undisguise(bs[i]));
Packit d28291
            a->Test( i );
Packit d28291
#           if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER)
Packit d28291
              // Workaround for ASan/MSan: the linker uses operator delete
Packit d28291
              // implementation from libclang_rt instead of gc_cpp (thus
Packit d28291
              // causing incompatible alloc/free).
Packit d28291
              GC_FREE(a);
Packit d28291
#           else
Packit d28291
              delete a;
Packit d28291
#           endif
Packit d28291
            b->Test( i );
Packit d28291
            B::Deleting( 1 );
Packit d28291
            delete b;
Packit d28291
            B::Deleting( 0 );
Packit d28291
#           ifdef FINALIZE_ON_DEMAND
Packit d28291
                 GC_invoke_finalizers();
Packit d28291
#           endif
Packit d28291
            }
Packit d28291
Packit d28291
            /* Make sure most of the finalizable Cs, Ds, and Fs have
Packit d28291
            gone away. */
Packit d28291
        C::Test();
Packit d28291
        D::Test();
Packit d28291
        F::Test();}
Packit d28291
Packit d28291
#   ifndef DONT_USE_STD_ALLOCATOR
Packit d28291
      x = *xptr;
Packit d28291
#   endif
Packit d28291
    my_assert (29 == x[0]);
Packit d28291
    GC_printf( "The test appears to have succeeded.\n" );
Packit d28291
    return( 0 );
Packit d28291
}