Home | History | Annotate | Download | only in eh
      1 /************************************************************************************************
      2  NC_ALLOC.CPP
      3 
      4  * Copyright (c) 1997
      5  * Mark of the Unicorn, Inc.
      6  *
      7  * Permission to use, copy, modify, distribute and sell this software
      8  * and its documentation for any purpose is hereby granted without fee,
      9  * provided that the above copyright notice appear in all copies and
     10  * that both that copyright notice and this permission notice appear
     11  * in supporting documentation.  Mark of the Unicorn makes no
     12  * representations about the suitability of this software for any
     13  * purpose.  It is provided "as is" without express or implied warranty.
     14 
     15 ************************************************************************************************/
     16 
     17 #include "nc_alloc.h"
     18 #include <string>
     19 
     20 #if defined (EH_NEW_HEADERS)
     21 #  include <new>
     22 #  include <cassert>
     23 #  include <cstdlib>
     24 #else
     25 #  include <assert.h>
     26 #  include <stdlib.h>
     27 #  include <new.h>
     28 #endif
     29 
     30 #if defined (EH_NEW_IOSTREAMS)
     31 #  include <iostream>
     32 #else
     33 #  include <iostream.h>
     34 #endif
     35 
     36 long alloc_count = 0;
     37 long object_count = 0;
     38 long TestController::possible_failure_count = 0;
     39 const char* TestController::current_test = "<unknown>";
     40 const char* TestController::current_test_category = "no category";
     41 const char* TestController::current_container = 0;
     42 bool  TestController::nc_verbose = true;
     43 bool  TestController::never_fail = false;
     44 bool  TestController::track_allocations = false;
     45 bool  TestController::leak_detection_enabled = false;
     46 TestController gTestController;
     47 
     48 //************************************************************************************************
     49 void TestController::maybe_fail(long) {
     50   if (never_fail || Failure_threshold() == kNotInExceptionTest)
     51     return;
     52 
     53   // throw if allocation would satisfy the threshold
     54   if (possible_failure_count++ >= Failure_threshold()) {
     55     // what about doing some standard new_handler() behavior here (to test it!) ???
     56 
     57     // reset and simulate an out-of-memory failure
     58     Failure_threshold() = kNotInExceptionTest;
     59 #ifndef EH_NO_EXCEPTIONS
     60     throw EH_STD::bad_alloc();
     61 #endif
     62   }
     63 }
     64 
     65 #if defined (EH_HASHED_CONTAINERS_IMPLEMENTED)
     66 #  if defined (__SGI_STL)
     67 #    if defined (EH_NEW_HEADERS)
     68 #      include <hash_set>
     69 #    else
     70 #      include <hash_set.h>
     71 #    endif
     72 #  elif defined (__MSL__)
     73 #    include <hashset.h>
     74 #  else
     75 #    error what do I include to get hash_set?
     76 #  endif
     77 #else
     78 #  if defined (EH_NEW_HEADERS)
     79 #    include <set>
     80 #  else
     81 #    include <set.h>
     82 #  endif
     83 #endif
     84 
     85 #if !defined (EH_HASHED_CONTAINERS_IMPLEMENTED)
     86 typedef EH_STD::set<void*, EH_STD::less<void*> > allocation_set;
     87 #else
     88 
     89 USING_CSTD_NAME(size_t)
     90 
     91 struct hash_void {
     92   size_t operator()(void* x) const { return (size_t)x; }
     93 };
     94 
     95 typedef EH_STD::hash_set<void*, ::hash_void, EH_STD::equal_to<void*> > allocation_set;
     96 #endif
     97 
     98 static allocation_set& alloc_set() {
     99   static allocation_set s;
    100   return s;
    101 }
    102 
    103 // Prevents infinite recursion during allocation
    104 static bool using_alloc_set = false;
    105 
    106 #if !defined (NO_FAST_ALLOCATOR)
    107 //
    108 //  FastAllocator -- speeds up construction of TestClass objects when
    109 // TESTCLASS_DEEP_DATA is enabled, and speeds up tracking of allocations
    110 // when the suite is run with the -t option.
    111 //
    112 class FastAllocator {
    113 public:
    114   //FastAllocator() : mFree(0), mUsed(0) {}
    115   static void *Allocate(size_t s) {
    116     void *result = 0;
    117 
    118     if (s <= sizeof(Block)) {
    119       if (mFree != 0) {
    120         result = mFree;
    121         mFree = mFree->next;
    122       }
    123       else if (mBlocks != 0 && mUsed < kBlockCount) {
    124         result =  (void*)&mBlocks[mUsed++];
    125       }
    126     }
    127     return result;
    128   }
    129 
    130   static bool Free(void* p) {
    131     Block* b = (Block*)p;
    132     if (mBlocks == 0 || b < mBlocks || b >= mBlocks + kBlockCount)
    133       return false;
    134     b->next = mFree;
    135     mFree = b;
    136     return true;
    137   }
    138 
    139   struct Block;
    140   friend struct Block;
    141 
    142   enum {
    143     // Number of fast allocation blocks to create.
    144     kBlockCount = 1500,
    145 
    146     // You may need to adjust this number for your platform.
    147     // A good choice will speed tests. A bad choice will still work.
    148     kMinBlockSize = 48
    149   };
    150 
    151   struct Block {
    152     union {
    153       Block *next;
    154       double dummy; // fbp - force alignment
    155       char dummy2[kMinBlockSize];
    156     };
    157   };
    158 
    159   static Block* mBlocks;
    160   static Block *mFree;
    161   static size_t mUsed;
    162 };
    163 
    164 FastAllocator::Block *FastAllocator::mBlocks =
    165 (FastAllocator::Block*)EH_CSTD::calloc( sizeof(FastAllocator::Block), FastAllocator::kBlockCount );
    166 FastAllocator::Block *FastAllocator::mFree;
    167 size_t FastAllocator::mUsed;
    168 
    169 
    170 static FastAllocator gFastAllocator;
    171 #endif
    172 
    173 inline char* AllocateBlock(size_t s) {
    174 #if !defined (NO_FAST_ALLOCATOR)
    175   char * const p = (char*)gFastAllocator.Allocate( s );
    176   if (p != 0)
    177     return p;
    178 #endif
    179 
    180   return (char*)EH_CSTD::malloc(s);
    181 }
    182 
    183 static void* OperatorNew( size_t s ) {
    184   if (!using_alloc_set) {
    185     simulate_possible_failure();
    186     ++alloc_count;
    187   }
    188 
    189   char *p = AllocateBlock(s);
    190 
    191   if (gTestController.TrackingEnabled() &&
    192       gTestController.LeakDetectionEnabled() &&
    193       !using_alloc_set) {
    194     using_alloc_set = true;
    195     bool inserted = alloc_set().insert(p).second;
    196     // Suppress warning about unused variable.
    197     inserted;
    198     EH_ASSERT(inserted);
    199     using_alloc_set = false;
    200   }
    201 
    202   return p;
    203 }
    204 
    205 void* _STLP_CALL operator new(size_t s)
    206 #ifdef EH_DELETE_HAS_THROW_SPEC
    207 throw(EH_STD::bad_alloc)
    208 #endif
    209 { return OperatorNew( s ); }
    210 
    211 #ifdef EH_USE_NOTHROW
    212 void* _STLP_CALL operator new(size_t size, const EH_STD::nothrow_t&) throw() {
    213   try {
    214     return OperatorNew( size );
    215   }
    216   catch (...) {
    217     return 0;
    218   }
    219 }
    220 #endif
    221 
    222 #if 1 /* defined (EH_VECTOR_OPERATOR_NEW) */
    223 void* _STLP_CALL operator new[](size_t size ) throw(EH_STD::bad_alloc) {
    224   return OperatorNew( size );
    225 }
    226 
    227 #  ifdef EH_USE_NOTHROW
    228 void* _STLP_CALL operator new[](size_t size, const EH_STD::nothrow_t&) throw() {
    229   try {
    230     return OperatorNew(size);
    231   }
    232   catch (...) {
    233     return 0;
    234   }
    235 }
    236 #  endif
    237 
    238 void _STLP_CALL operator delete[](void* ptr) throw()
    239 { operator delete( ptr ); }
    240 #endif
    241 
    242 #if defined (EH_DELETE_HAS_THROW_SPEC)
    243 void _STLP_CALL operator delete(void* s) throw()
    244 #else
    245 void _STLP_CALL operator delete(void* s)
    246 #endif
    247 {
    248   if ( s != 0 ) {
    249     if ( !using_alloc_set ) {
    250       --alloc_count;
    251 
    252       if ( gTestController.TrackingEnabled() && gTestController.LeakDetectionEnabled() ) {
    253         using_alloc_set = true;
    254         allocation_set::iterator p = alloc_set().find( (char*)s );
    255         EH_ASSERT( p != alloc_set().end() );
    256         alloc_set().erase( p );
    257         using_alloc_set = false;
    258       }
    259     }
    260 # if ! defined (NO_FAST_ALLOCATOR)
    261     if ( !gFastAllocator.Free( s ) )
    262 # endif
    263       EH_CSTD::free(s);
    264   }
    265 }
    266 
    267 
    268 /*===================================================================================
    269   ClearAllocationSet  (private helper)
    270 
    271   EFFECTS:  Empty the set of allocated blocks.
    272 ====================================================================================*/
    273 void TestController::ClearAllocationSet() {
    274   if (!using_alloc_set) {
    275     using_alloc_set = true;
    276     alloc_set().clear();
    277     using_alloc_set = false;
    278   }
    279 }
    280 
    281 
    282 bool TestController::ReportLeaked() {
    283   EndLeakDetection();
    284 
    285   EH_ASSERT( !using_alloc_set || (alloc_count == static_cast<int>(alloc_set().size())) );
    286 
    287   if (alloc_count != 0 || object_count != 0) {
    288     EH_STD::cerr<<"\nEH TEST FAILURE !\n";
    289     PrintTestName(true);
    290     if (alloc_count)
    291       EH_STD::cerr << "ERROR : " << alloc_count << " outstanding allocations.\n";
    292     if (object_count)
    293       EH_STD::cerr << "ERROR : " << object_count << " non-destroyed objects.\n";
    294     alloc_count = object_count = 0;
    295     return true;
    296   }
    297   return false;
    298 }
    299 
    300 
    301 
    302 /*===================================================================================
    303   PrintTestName
    304 
    305   EFFECTS: Prints information about the current test. If err is false, ends with
    306     an ellipsis, because the test is ongoing. If err is true an error is being
    307     reported, and the output ends with an endl.
    308 ====================================================================================*/
    309 
    310 void TestController::PrintTestName(bool err) {
    311   if (current_container)
    312     EH_STD::cerr<<"["<<current_container<<"] :";
    313   EH_STD::cerr<<"testing "<<current_test <<" (" << current_test_category <<")";
    314   if (err)
    315     EH_STD::cerr<<EH_STD::endl;
    316   else
    317     EH_STD::cerr<<" ... ";
    318 }
    319 
    320 void TestController::ReportSuccess(int count) {
    321   if (nc_verbose)
    322     EH_STD::cerr<<(count+1)<<" try successful"<<EH_STD::endl;
    323 }
    324 
    325 long& TestController::Failure_threshold() {
    326   static long failure_threshold = kNotInExceptionTest;
    327   return failure_threshold;
    328 }
    329