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 #if defined (EH_DELETE_HAS_THROW_SPEC)
268 void _STLP_CALL operator delete(void* s, uintptr_t) throw()
269 #else
270 void _STLP_CALL operator delete(void* s, uintptr_t)
271 #endif
272 {
273 	::operator delete(s);
274 }
275 
276 /*===================================================================================
277   ClearAllocationSet  (private helper)
278 
279   EFFECTS:  Empty the set of allocated blocks.
280 ====================================================================================*/
281 void TestController::ClearAllocationSet() {
282   if (!using_alloc_set) {
283     using_alloc_set = true;
284     alloc_set().clear();
285     using_alloc_set = false;
286   }
287 }
288 
289 
290 bool TestController::ReportLeaked() {
291   EndLeakDetection();
292 
293   EH_ASSERT( !using_alloc_set || (alloc_count == static_cast<int>(alloc_set().size())) );
294 
295   if (alloc_count != 0 || object_count != 0) {
296     EH_STD::cerr<<"\nEH TEST FAILURE !\n";
297     PrintTestName(true);
298     if (alloc_count)
299       EH_STD::cerr << "ERROR : " << alloc_count << " outstanding allocations.\n";
300     if (object_count)
301       EH_STD::cerr << "ERROR : " << object_count << " non-destroyed objects.\n";
302     alloc_count = object_count = 0;
303     return true;
304   }
305   return false;
306 }
307 
308 
309 
310 /*===================================================================================
311   PrintTestName
312 
313   EFFECTS: Prints information about the current test. If err is false, ends with
314     an ellipsis, because the test is ongoing. If err is true an error is being
315     reported, and the output ends with an endl.
316 ====================================================================================*/
317 
318 void TestController::PrintTestName(bool err) {
319   if (current_container)
320     EH_STD::cerr<<"["<<current_container<<"] :";
321   EH_STD::cerr<<"testing "<<current_test <<" (" << current_test_category <<")";
322   if (err)
323     EH_STD::cerr<<EH_STD::endl;
324   else
325     EH_STD::cerr<<" ... ";
326 }
327 
328 void TestController::ReportSuccess(int count) {
329   if (nc_verbose)
330     EH_STD::cerr<<(count+1)<<" try successful"<<EH_STD::endl;
331 }
332 
333 long& TestController::Failure_threshold() {
334   static long failure_threshold = kNotInExceptionTest;
335   return failure_threshold;
336 }
337