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