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