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 //************************************************************************************************
maybe_fail(long)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 {
operator ()hash_void92   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 
alloc_set()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) {}
Allocate(size_t s)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 
Free(void * p)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 
AllocateBlock(size_t s)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 
OperatorNew(size_t s)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 
operator new(size_t s)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
operator new(size_t size,const EH_STD::nothrow_t &)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) */
operator new[](size_t size)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
operator new[](size_t size,const EH_STD::nothrow_t &)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 
operator delete[](void * ptr)238 void _STLP_CALL operator delete[](void* ptr) throw()
239 { operator delete( ptr ); }
240 #endif
241 
242 #if defined (EH_DELETE_HAS_THROW_SPEC)
operator delete(void * s)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 ====================================================================================*/
ClearAllocationSet()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 
ReportLeaked()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 
PrintTestName(bool err)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 
ReportSuccess(int count)320 void TestController::ReportSuccess(int count) {
321   if (nc_verbose)
322     EH_STD::cerr<<(count+1)<<" try successful"<<EH_STD::endl;
323 }
324 
Failure_threshold()325 long& TestController::Failure_threshold() {
326   static long failure_threshold = kNotInExceptionTest;
327   return failure_threshold;
328 }
329