|
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 |
}
|