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 #if defined (EH_DELETE_HAS_THROW_SPEC)
operator delete(void * s,uintptr_t)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 ====================================================================================*/
ClearAllocationSet()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
ReportLeaked()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
PrintTestName(bool err)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
ReportSuccess(int count)328 void TestController::ReportSuccess(int count) {
329 if (nc_verbose)
330 EH_STD::cerr<<(count+1)<<" try successful"<<EH_STD::endl;
331 }
332
Failure_threshold()333 long& TestController::Failure_threshold() {
334 static long failure_threshold = kNotInExceptionTest;
335 return failure_threshold;
336 }
337