375 lines
8.4 KiB
C++
375 lines
8.4 KiB
C++
|
/************************************************************************************************
|
||
|
NC_ALLOC.CPP
|
||
|
|
||
|
* Copyright (c) 1997
|
||
|
* Mark of the Unicorn, Inc.
|
||
|
*
|
||
|
* Permission to use, copy, modify, distribute and sell this software
|
||
|
* and its documentation for any purpose is hereby granted without fee,
|
||
|
* provided that the above copyright notice appear in all copies and
|
||
|
* that both that copyright notice and this permission notice appear
|
||
|
* in supporting documentation. Mark of the Unicorn makes no
|
||
|
* representations about the suitability of this software for any
|
||
|
* purpose. It is provided "as is" without express or implied warranty.
|
||
|
|
||
|
************************************************************************************************/
|
||
|
|
||
|
#include "nc_alloc.h"
|
||
|
#include <string>
|
||
|
|
||
|
#if defined (EH_NEW_HEADERS)
|
||
|
# include <new>
|
||
|
# include <cassert>
|
||
|
# include <cstdlib>
|
||
|
#else
|
||
|
# include <assert.h>
|
||
|
# include <stdlib.h>
|
||
|
# include <new.h>
|
||
|
#endif
|
||
|
|
||
|
#if defined (EH_NEW_IOSTREAMS)
|
||
|
# include <iostream>
|
||
|
#else
|
||
|
# include <iostream.h>
|
||
|
#endif
|
||
|
|
||
|
long alloc_count = 0;
|
||
|
long object_count = 0;
|
||
|
long TestController::possible_failure_count = 0;
|
||
|
const char* TestController::current_test = "<unknown>";
|
||
|
const char* TestController::current_test_category = "no category";
|
||
|
const char* TestController::current_container = 0;
|
||
|
bool TestController::nc_verbose = true;
|
||
|
bool TestController::never_fail = false;
|
||
|
bool TestController::track_allocations = false;
|
||
|
bool TestController::leak_detection_enabled = false;
|
||
|
TestController gTestController;
|
||
|
|
||
|
//************************************************************************************************
|
||
|
void TestController::maybe_fail(long)
|
||
|
{
|
||
|
if ( never_fail || Failure_threshold() == kNotInExceptionTest )
|
||
|
return;
|
||
|
|
||
|
// throw if allocation would satisfy the threshold
|
||
|
if ( possible_failure_count++ >= Failure_threshold() )
|
||
|
{
|
||
|
|
||
|
// what about doing some standard new_handler() behavior here (to test it!) ???
|
||
|
|
||
|
// reset and simulate an out-of-memory failure
|
||
|
Failure_threshold() = kNotInExceptionTest;
|
||
|
# ifndef EH_NO_EXCEPTIONS
|
||
|
throw EH_STD::bad_alloc();
|
||
|
# endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# if defined( EH_HASHED_CONTAINERS_IMPLEMENTED )
|
||
|
|
||
|
# if defined (__SGI_STL )
|
||
|
|
||
|
# if defined (EH_NEW_HEADERS)
|
||
|
# include <hash_set>
|
||
|
# else
|
||
|
# include <hash_set.h>
|
||
|
# endif
|
||
|
# elif defined (__MSL__)
|
||
|
# include <hashset.h>
|
||
|
# else
|
||
|
# error what do I include to get hash_set?
|
||
|
# endif
|
||
|
|
||
|
# else
|
||
|
|
||
|
# if defined (EH_NEW_HEADERS)
|
||
|
# include <set>
|
||
|
# else
|
||
|
# include <set.h>
|
||
|
# endif
|
||
|
# endif
|
||
|
|
||
|
# if !defined( EH_HASHED_CONTAINERS_IMPLEMENTED )
|
||
|
typedef EH_STD::set<void*, EH_STD::less<void*> > allocation_set;
|
||
|
#else
|
||
|
|
||
|
USING_CSTD_NAME(size_t)
|
||
|
|
||
|
struct hash_void
|
||
|
{
|
||
|
size_t operator()(void* x) const { return (size_t)x; }
|
||
|
};
|
||
|
|
||
|
typedef std::hash_set<void*, ::hash_void, std::equal_to<void*> > allocation_set;
|
||
|
# endif
|
||
|
|
||
|
static allocation_set& alloc_set()
|
||
|
{
|
||
|
static allocation_set s;
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
// Prevents infinite recursion during allocation
|
||
|
static bool using_alloc_set = false;
|
||
|
|
||
|
# if !defined (NO_FAST_ALLOCATOR)
|
||
|
//
|
||
|
// FastAllocator -- speeds up construction of TestClass objects when
|
||
|
// TESTCLASS_DEEP_DATA is enabled, and speeds up tracking of allocations
|
||
|
// when the suite is run with the -t option.
|
||
|
//
|
||
|
class FastAllocator
|
||
|
{
|
||
|
public:
|
||
|
// FastAllocator() : mFree(0), mUsed(0) {}
|
||
|
|
||
|
static void *Allocate( size_t s )
|
||
|
{
|
||
|
void *result = 0;
|
||
|
|
||
|
if ( s <= sizeof( Block ) )
|
||
|
{
|
||
|
if ( mFree != 0 )
|
||
|
{
|
||
|
result = mFree;
|
||
|
mFree = mFree->next;
|
||
|
}
|
||
|
else if ( mBlocks != 0 && mUsed < kBlockCount )
|
||
|
{
|
||
|
result = (void*)&mBlocks[mUsed++];
|
||
|
}
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
static bool Free( void* p )
|
||
|
{
|
||
|
Block* b = (Block*)p;
|
||
|
if ( mBlocks == 0 || b < mBlocks || b >= mBlocks + kBlockCount )
|
||
|
return false;
|
||
|
b->next = mFree;
|
||
|
mFree = b;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
struct Block;
|
||
|
friend struct Block;
|
||
|
|
||
|
enum
|
||
|
{
|
||
|
// Number of fast allocation blocks to create.
|
||
|
kBlockCount = 1500,
|
||
|
|
||
|
// You may need to adjust this number for your platform.
|
||
|
// A good choice will speed tests. A bad choice will still work.
|
||
|
kMinBlockSize = 48
|
||
|
};
|
||
|
|
||
|
struct Block
|
||
|
{
|
||
|
union {
|
||
|
Block *next;
|
||
|
double dummy; // fbp - force alignment
|
||
|
char dummy2[kMinBlockSize];
|
||
|
};
|
||
|
};
|
||
|
|
||
|
static Block* mBlocks;
|
||
|
static Block *mFree;
|
||
|
static size_t mUsed;
|
||
|
};
|
||
|
|
||
|
FastAllocator::Block *FastAllocator::mBlocks =
|
||
|
(FastAllocator::Block*)EH_CSTD::calloc( sizeof(FastAllocator::Block), FastAllocator::kBlockCount );
|
||
|
FastAllocator::Block *FastAllocator::mFree;
|
||
|
size_t FastAllocator::mUsed;
|
||
|
|
||
|
|
||
|
static FastAllocator gFastAllocator;
|
||
|
# endif
|
||
|
|
||
|
inline char* AllocateBlock( size_t s )
|
||
|
{
|
||
|
# if !defined (NO_FAST_ALLOCATOR)
|
||
|
char * const p = (char*)gFastAllocator.Allocate( s );
|
||
|
if ( p != 0 )
|
||
|
return p;
|
||
|
# endif
|
||
|
|
||
|
return (char*)EH_CSTD::malloc(s);
|
||
|
}
|
||
|
|
||
|
static void* OperatorNew( size_t s )
|
||
|
{
|
||
|
if ( !using_alloc_set )
|
||
|
{
|
||
|
simulate_possible_failure();
|
||
|
alloc_count++;
|
||
|
}
|
||
|
|
||
|
char *p = AllocateBlock(s);
|
||
|
|
||
|
if ( gTestController.TrackingEnabled()
|
||
|
&& gTestController.LeakDetectionEnabled()
|
||
|
&& !using_alloc_set )
|
||
|
{
|
||
|
using_alloc_set = true;
|
||
|
EH_ASSERT( alloc_set().find( p ) == alloc_set().end() );
|
||
|
alloc_set().insert( p );
|
||
|
using_alloc_set = false;
|
||
|
}
|
||
|
|
||
|
return p;
|
||
|
}
|
||
|
|
||
|
void* _STLP_CALL operator new(size_t s)
|
||
|
#ifdef EH_DELETE_HAS_THROW_SPEC
|
||
|
throw(EH_STD::bad_alloc)
|
||
|
#endif
|
||
|
{
|
||
|
return OperatorNew( s );
|
||
|
}
|
||
|
|
||
|
#ifdef EH_USE_NOTHROW
|
||
|
void* _STLP_CALL operator new(size_t size, const EH_STD::nothrow_t&) throw()
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
return OperatorNew( size );
|
||
|
}
|
||
|
catch(...)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
# if defined (EH_VECTOR_OPERATOR_NEW)
|
||
|
void* _STLP_CALL operator new[](size_t size ) throw(EH_STD::bad_alloc)
|
||
|
{
|
||
|
return OperatorNew( size );
|
||
|
}
|
||
|
|
||
|
#ifdef EH_USE_NOTHROW
|
||
|
void* _STLP_CALL operator new[](size_t size, const EH_STD::nothrow_t&) throw()
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
return OperatorNew( size );
|
||
|
}
|
||
|
catch(...)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
void _STLP_CALL operator delete[](void* ptr) throw()
|
||
|
{
|
||
|
operator delete( ptr );
|
||
|
}
|
||
|
# endif
|
||
|
|
||
|
# if defined (EH_DELETE_HAS_THROW_SPEC)
|
||
|
void _STLP_CALL operator delete(void* s) throw()
|
||
|
# else
|
||
|
void _STLP_CALL operator delete(void* s)
|
||
|
# endif
|
||
|
{
|
||
|
if ( s != 0 )
|
||
|
{
|
||
|
if ( !using_alloc_set )
|
||
|
{
|
||
|
alloc_count--;
|
||
|
|
||
|
if ( gTestController.TrackingEnabled() && gTestController.LeakDetectionEnabled() )
|
||
|
{
|
||
|
using_alloc_set = true;
|
||
|
allocation_set::iterator p = alloc_set().find( (char*)s );
|
||
|
EH_ASSERT( p != alloc_set().end() );
|
||
|
alloc_set().erase( p );
|
||
|
using_alloc_set = false;
|
||
|
}
|
||
|
}
|
||
|
# if ! defined (NO_FAST_ALLOCATOR)
|
||
|
if ( !gFastAllocator.Free( s ) )
|
||
|
# endif
|
||
|
EH_CSTD::free(s);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*===================================================================================
|
||
|
ClearAllocationSet (private helper)
|
||
|
|
||
|
EFFECTS: Empty the set of allocated blocks.
|
||
|
====================================================================================*/
|
||
|
void TestController::ClearAllocationSet()
|
||
|
{
|
||
|
if ( !using_alloc_set )
|
||
|
{
|
||
|
using_alloc_set = true;
|
||
|
alloc_set().clear();
|
||
|
using_alloc_set = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
bool TestController::ReportLeaked()
|
||
|
{
|
||
|
|
||
|
EndLeakDetection();
|
||
|
|
||
|
if (using_alloc_set)
|
||
|
EH_ASSERT( alloc_count == static_cast<int>(alloc_set().size()) );
|
||
|
|
||
|
if ( alloc_count!=0 || object_count!=0 )
|
||
|
{
|
||
|
EH_STD::cerr<<"\nEH TEST FAILURE !\n";
|
||
|
PrintTestName(true);
|
||
|
if (alloc_count)
|
||
|
EH_STD::cerr<<"ERROR : "<<alloc_count<<" outstanding allocations.\n";
|
||
|
if (object_count)
|
||
|
EH_STD::cerr<<"ERROR : "<<object_count<<" non-destroyed objects.\n";
|
||
|
alloc_count = object_count = 0;
|
||
|
|
||
|
return true;
|
||
|
|
||
|
}
|
||
|
return false;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*===================================================================================
|
||
|
PrintTestName
|
||
|
|
||
|
EFFECTS: Prints information about the current test. If err is false, ends with
|
||
|
an ellipsis, because the test is ongoing. If err is true an error is being
|
||
|
reported, and the output ends with an endl.
|
||
|
====================================================================================*/
|
||
|
|
||
|
void
|
||
|
TestController::PrintTestName(bool err) {
|
||
|
if (current_container)
|
||
|
EH_STD::cerr<<"["<<current_container<<"] :";
|
||
|
EH_STD::cerr<<"testing "<<current_test <<" (" << current_test_category <<")";
|
||
|
if (err)
|
||
|
EH_STD::cerr<<EH_STD::endl;
|
||
|
else
|
||
|
EH_STD::cerr<<" ... ";
|
||
|
}
|
||
|
|
||
|
void TestController::ReportSuccess(int count) {
|
||
|
if (nc_verbose)
|
||
|
EH_STD::cerr<<(count+1)<<" try successful"<<EH_STD::endl;
|
||
|
}
|
||
|
|
||
|
long& TestController::Failure_threshold()
|
||
|
{
|
||
|
static long failure_threshold = kNotInExceptionTest;
|
||
|
return failure_threshold;
|
||
|
}
|
||
|
|