GRASS GIS 8 Programmer's Manual 8.3.2(2024)-exported
Loading...
Searching...
No Matches
mm.cpp
Go to the documentation of this file.
1/****************************************************************************
2 *
3 * MODULE: iostream
4 *
5
6 * COPYRIGHT (C) 2007 Laura Toma
7 *
8 *
9
10 * Iostream is a library that implements streams, external memory
11 * sorting on streams, and an external memory priority queue on
12 * streams. These are the fundamental components used in external
13 * memory algorithms.
14
15 * Credits: The library was developed by Laura Toma. The kernel of
16 * class STREAM is based on the similar class existent in the GPL TPIE
17 * project developed at Duke University. The sorting and priority
18 * queue have been developed by Laura Toma based on communications
19 * with Rajiv Wickremesinghe. The library was developed as part of
20 * porting Terraflow to GRASS in 2001. PEARL upgrades in 2003 by
21 * Rajiv Wickremesinghe as part of the Terracost project.
22
23 *
24 * This program is free software; you can redistribute it and/or modify
25 * it under the terms of the GNU General Public License as published by
26 * the Free Software Foundation; either version 2 of the License, or
27 * (at your option) any later version.
28 *
29
30 * This program is distributed in the hope that it will be useful,
31 * but WITHOUT ANY WARRANTY; without even the implied warranty of
32 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
33 * General Public License for more details. *
34 * **************************************************************************/
35
36// A simple registration based memory manager.
37
38#include <stdio.h>
39#include <stdlib.h>
40#include <assert.h>
41#include <iostream>
42using std::cerr;
43using std::cout;
44using std::endl;
45
46// #include <mm.h>
47#include <grass/iostream/mm.h>
48
49#define MM_DEBUG if (0)
50
51/* ************************************************************ */
52MM_register::MM_register()
53{
54
55 instances++;
56 if (instances > 1) {
57 cerr << "MM_register(): Only 1 instance of MM_register should exist.\n";
58 assert(0); // core dump if debugging
59 exit(1);
60 }
61 assert(instances == 1);
62
63 // by default, we ignore if memory limit is exceeded
64 register_new = MM_IGNORE_MEMORY_EXCEEDED;
65}
66
67/* ************************************************************ */
68MM_register::~MM_register(void)
69{
70
71 if (instances > 1) {
72 cerr << "MM_register(): Only 1 instance of MM_register should exist.\n";
73 assert(0); // core dump if debugging
74 exit(1);
75 }
76 assert(instances == 1);
77 instances--;
78}
79
80/* ************************************************************ */
81void MM_register::print()
82{
83
84 size_t availMB = (remaining >> 20);
85 if (remaining) {
86 cout << "available memory: " << availMB << "MB "
87 << "(" << remaining << "B)" << endl;
88 }
89 else {
90 cout << "available memory: " << remaining
91 << "B, exceeding: " << used - user_limit << "B" << endl;
92 }
93}
94
95/* ************************************************************ */
96// User-callable method to set allowable memory size
97MM_err MM_register::set_memory_limit(size_t new_limit)
98{
99
100 assert(new_limit > 0);
101 if (used > new_limit) {
102 // return MM_ERROR_EXCESSIVE_ALLOCATION;
103 switch (register_new) {
104 case MM_ABORT_ON_MEMORY_EXCEEDED:
105 cerr << " MM_register::set_memory_limit to " << new_limit
106 << ", used " << used << ". allocation exceeds new limit.\n";
107 cerr.flush();
108 assert(0); // core dump if debugging
109 exit(1);
110 break;
111
112 case MM_WARN_ON_MEMORY_EXCEEDED:
113 cerr << " MM_register::set_memory_limit to " << new_limit
114 << ", used " << used << ". allocation exceeds new limit.\n";
115 break;
116
117 case MM_IGNORE_MEMORY_EXCEEDED:
118 break;
119 }
120 user_limit = new_limit;
121 remaining = 0;
122 return MM_ERROR_NO_ERROR;
123 }
124
125 assert(used <= new_limit);
126 // These are unsigned, so be careful.
127 if (new_limit < user_limit) {
128 remaining -= user_limit - new_limit;
129 }
130 else {
131 remaining += new_limit - user_limit;
132 }
133 user_limit = new_limit;
134 return MM_ERROR_NO_ERROR;
135}
136
137/* ************************************************************ */
138// only warn if memory limit exceeded
139void MM_register::warn_memory_limit()
140{
141 register_new = MM_WARN_ON_MEMORY_EXCEEDED;
142}
143
144/* ************************************************************ */
145// abort if memory limit exceeded
146void MM_register::enforce_memory_limit()
147{
148 register_new = MM_ABORT_ON_MEMORY_EXCEEDED;
149
150 if (used > user_limit) {
151 cerr << " MM_register::enforce_memory_limit: limit=" << user_limit
152 << ", used=" << used << ". allocation exceeds limit.\n";
153 assert(0); // core dump if debugging
154 exit(1);
155 }
156}
157
158/* ************************************************************ */
159// ignore memory limit accounting
160void MM_register::ignore_memory_limit()
161{
162 register_new = MM_IGNORE_MEMORY_EXCEEDED;
163}
164
165/* ************************************************************ */
166// provide accounting state
167MM_mode MM_register::get_limit_mode()
168{
169 return register_new;
170}
171
172/* ************************************************************ */
173// provide print ccounting state
174void MM_register::print_limit_mode()
175{
176 cout << "Memory manager registering memory in ";
177 switch (register_new) {
178 case MM_ABORT_ON_MEMORY_EXCEEDED:
179 cout << "MM_ABORT_ON_MEMORY_EXCEEDED";
180 break;
181 case MM_WARN_ON_MEMORY_EXCEEDED:
182 cout << "MM_WARN_ON_MEMORY_EXCEEDED";
183 break;
184 case MM_IGNORE_MEMORY_EXCEEDED:
185 cout << "MM_IGNORE_MEMORY_EXCEEDED";
186 break;
187 }
188 cout << " mode." << endl;
189}
190
191/* ************************************************************ */
192// return the amount of memory available before user-specified memory
193// limit will be exceeded
194size_t MM_register::memory_available()
195{
196 return remaining;
197}
198
199/* ************************************************************ */
200size_t MM_register::memory_used()
201{
202 return used;
203}
204
205/* ************************************************************ */
206size_t MM_register::memory_limit()
207{
208 return user_limit;
209}
210
211/* ---------------------------------------------------------------------- */
212// return the overhead on each memory allocation request
213
214// SIZE_SPACE is to ensure alignment on quad word boundaries. It may be
215// possible to check whether a machine needs this at configuration
216// time or if dword alignment is ok. On the HP 9000, bus errors occur
217// when loading doubles that are not qword aligned.
218static const size_t SIZE_SPACE = (sizeof(size_t) > 8 ? sizeof(size_t) : 8);
219
220int MM_register::space_overhead()
221{
222 return SIZE_SPACE;
223}
224
225/* ************************************************************ */
226// check that new allocation request is below user-defined limit.
227// This should be a private method, only called by operator new.
228MM_err MM_register::register_allocation(size_t request)
229{
230
231 if (request > remaining) {
232 remaining = 0;
233 used += request;
234 return MM_ERROR_INSUFFICIENT_SPACE;
235 }
236 else {
237 used += request;
238 remaining -= request;
239 return MM_ERROR_NO_ERROR;
240 }
241}
242
243/* ************************************************************ */
244// do the accounting for a memory deallocation request.
245// This should be a private method, only called by operators
246// delete and delete [].
247MM_err MM_register::register_deallocation(size_t sz)
248{
249
250 if (sz > used) {
251 used = 0;
252 remaining = user_limit;
253 return MM_ERROR_UNDERFLOW;
254 }
255 else {
256
257 used -= sz;
258 if (used < user_limit) {
259 remaining = user_limit - used;
260 }
261 else {
262 assert(remaining == 0);
263 }
264 return MM_ERROR_NO_ERROR;
265 }
266}
267
268/* ************************************************************ */
269/* these overloaded operators must only be used by this memory manager
270 * risk of invalid free if these operators are defined outside the MM_register
271 * class e.g. GDAL allocating memory with something else than new as defined
272 * here but then using delete as defined here
273 */
274#ifdef GRASS_MM_USE_EXCEPTION_SPECIFIER
275void *MM_register::operator new[](size_t sz) throw(std::bad_alloc)
276{
277#else
278void *MM_register::operator new[](size_t sz)
279{
280#endif /* GRASS_MM_USE_EXCEPTION_SPECIFIER */
281 void *p;
282
283 MM_DEBUG cout << "new: sz=" << sz << ", register " << sz + SIZE_SPACE
284 << "B ,";
285
286 if (MM_manager.register_allocation(sz + SIZE_SPACE) != MM_ERROR_NO_ERROR) {
287 // must be MM_ERROR_INSUF_SPACE
288 switch (MM_manager.register_new) {
289
290 case MM_ABORT_ON_MEMORY_EXCEEDED:
291 cerr << "MM error: limit =" << MM_manager.memory_limit() << "B. "
292 << "allocating " << sz << "B. "
293 << "limit exceeded by "
294 << MM_manager.memory_used() - MM_manager.memory_limit() << "B."
295 << endl;
296 assert(0); // core dump if debugging
297 exit(1);
298 break;
299
300 case MM_WARN_ON_MEMORY_EXCEEDED:
301 cerr << "MM warning: limit=" << MM_manager.memory_limit() << "B. "
302 << "allocating " << sz << "B. "
303 << " limit exceeded by "
304 << MM_manager.memory_used() - MM_manager.memory_limit() << "B."
305 << endl;
306 break;
307
308 case MM_IGNORE_MEMORY_EXCEEDED:
309 break;
310 }
311 }
312
313 p = malloc(sz + SIZE_SPACE);
314
315 if (!p) {
316 cerr << "new: out of memory while allocating " << sz << "B" << endl;
317 assert(0);
318 exit(1);
319 }
320
321 *((size_t *)p) = sz;
322
323 MM_DEBUG cout << "ptr=" << (void *)(((char *)p) + SIZE_SPACE) << endl;
324
325 return ((char *)p) + SIZE_SPACE;
326}
327
328/* ************************************************************ */
329#ifdef GRASS_MM_USE_EXCEPTION_SPECIFIER
330void *MM_register::operator new(size_t sz) throw(std::bad_alloc)
331{
332#else
333void *MM_register::operator new(size_t sz)
334{
335#endif /* GRASS_MM_USE_EXCEPTION_SPECIFIER */
336 void *p;
337
338 MM_DEBUG cout << "new: sz=" << sz << ", register " << sz + SIZE_SPACE
339 << "B ,";
340
341 if (MM_manager.register_allocation(sz + SIZE_SPACE) != MM_ERROR_NO_ERROR) {
342 // must be MM_ERROR_INSUF_SPACE
343 switch (MM_manager.register_new) {
344
345 case MM_ABORT_ON_MEMORY_EXCEEDED:
346 cerr << "MM error: limit =" << MM_manager.memory_limit() << "B. "
347 << "allocating " << sz << "B. "
348 << "limit exceeded by "
349 << MM_manager.memory_used() - MM_manager.memory_limit() << "B."
350 << endl;
351 assert(0); // core dump if debugging
352 exit(1);
353 break;
354
355 case MM_WARN_ON_MEMORY_EXCEEDED:
356 cerr << "MM warning: limit=" << MM_manager.memory_limit() << "B. "
357 << "allocating " << sz << "B. "
358 << " limit exceeded by "
359 << MM_manager.memory_used() - MM_manager.memory_limit() << "B."
360 << endl;
361 break;
362
363 case MM_IGNORE_MEMORY_EXCEEDED:
364 break;
365 }
366 }
367
368 p = malloc(sz + SIZE_SPACE);
369
370 if (!p) {
371 cerr << "new: out of memory while allocating " << sz << "B" << endl;
372 assert(0);
373 exit(1);
374 }
375
376 *((size_t *)p) = sz;
377
378 MM_DEBUG cout << "ptr=" << (void *)(((char *)p) + SIZE_SPACE) << endl;
379
380 return ((char *)p) + SIZE_SPACE;
381}
382
383/* ---------------------------------------------------------------------- */
384#ifdef GRASS_MM_USE_EXCEPTION_SPECIFIER
385void MM_register::operator delete(void *ptr) throw()
386{
387#else
388void MM_register::operator delete(void *ptr) noexcept
389{
390#endif /* GRASS_MM_USE_EXCEPTION_SPECIFIER */
391 size_t sz;
392 void *p;
393
394 MM_DEBUG cout << "delete: ptr=" << ptr << ",";
395
396 if (!ptr) {
397 cerr << "MM warning: operator delete was given a NULL pointer\n";
398 cerr.flush();
399 // this may actually happen: for instance when calling a default
400 // destructor for something that was not allocated with new
401 // e.g. ofstream str(name) ---- ~ofstream() called ==> ptr=NULL
402
403 // who wrote the above comment? -RW
404 assert(0);
405 // exit(1);
406 return;
407 }
408
409 assert(ptr);
410
411 /* causes invalid free if ptr has not been allocated with new as
412 * defined above */
413 p = ((char *)ptr) - SIZE_SPACE; // the base of memory
414 sz = *((size_t *)p);
415
416 MM_DEBUG cout << "size=" << sz << ", free " << p << "B and deallocate "
417 << sz + SIZE_SPACE << endl;
418
419 if (MM_manager.register_deallocation(sz + SIZE_SPACE) !=
420 MM_ERROR_NO_ERROR) {
421 // must be MM_ERROR_UNDERFLOW
422 cerr << "delete: MM_manager.register_deallocation failed\n";
423 assert(0);
424 exit(1);
425 }
426
427 free(p);
428}
429
430/* ---------------------------------------------------------------------- */
431#ifdef GRASS_MM_USE_EXCEPTION_SPECIFIER
432void MM_register::operator delete[](void *ptr) throw()
433{
434#else
435void MM_register::operator delete[](void *ptr) noexcept
436{
437#endif /* GRASS_MM_USE_EXCEPTION_SPECIFIER */
438 size_t sz;
439 void *p;
440
441 MM_DEBUG cout << "delete[]: ptr=" << ptr << ",";
442
443 if (!ptr) {
444 // can this happen? -- it does: see delete above
445 cerr << "MM warning: operator delete [] was given a NULL pointer\n";
446 cerr.flush();
447 // assert(0);
448 // exit(1);
449 return;
450 }
451 assert(ptr);
452
453 /* causes invalid free if ptr has not been allocated with new as
454 * defined above */
455 p = ((char *)ptr) - SIZE_SPACE; // the base of memory
456 sz = *((size_t *)p);
457
458 MM_DEBUG cout << "size=" << sz << ", free " << p << "B and deallocate "
459 << sz + SIZE_SPACE << endl;
460
461 if (MM_manager.register_deallocation(sz + SIZE_SPACE) !=
462 MM_ERROR_NO_ERROR) {
463 // must be MM_ERROR_UNDERFLOW
464 cerr << "delete[]: MM_manager.register_deallocation failed\n";
465 assert(0);
466 exit(1);
467 }
468
469 free(p);
470}
471
472/* ************************************************************ */
473// Instantiate the actual memory manager, and allocate the
474// its static data members
475MM_register MM_manager;
476int MM_register::instances = 0; // Number of instances. (init)
477// TPIE's "register memory requests" flag
478MM_mode MM_register::register_new = MM_IGNORE_MEMORY_EXCEEDED;
479// This causes constructors for static variables to fail
480// MM_mode MM_register::register_new = MM_ABORT_ON_MEMORY_EXCEEDED;
481
482/* ************************************************************ */
483// The counter of mm_register_init instances. It is implicitly set to 0.
484unsigned int mm_register_init::count;
485
486// The constructor and destructor that ensure that the memory manager is
487// created exactly once, and destroyed when appropriate.
488mm_register_init::mm_register_init(void)
489{
490 if (count++ == 0) {
491 MM_manager.set_memory_limit(MM_DEFAULT_MM_SIZE);
492 }
493}
494
495mm_register_init::~mm_register_init(void)
496{
497 --count;
498}
int count
#define assert(condition)
Definition lz4.c:393
#define MM_DEBUG
Definition mm.cpp:49
MM_register MM_manager
Definition mm.cpp:475