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