GRASS GIS 7 Programmer's Manual  7.8.5(2020)-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>
44 using std::cout;
45 using std::cerr;
46 using std::endl;
47 
48 //#include <mm.h>
49 #include <grass/iostream/mm.h>
50 
51 #define MM_DEBUG if(0)
52 
53 
54 
55 /* ************************************************************ */
56 MM_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 /* ************************************************************ */
73 MM_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 /* ************************************************************ */
86 void 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
103 MM_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
145 void MM_register::warn_memory_limit() {
146  register_new = MM_WARN_ON_MEMORY_EXCEEDED;
147 }
148 
149 
150 /* ************************************************************ */
151 //abort if memory limit exceeded
152 void 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
166 void MM_register::ignore_memory_limit() {
167  register_new = MM_IGNORE_MEMORY_EXCEEDED;
168 }
169 
170 
171 /* ************************************************************ */
172 // provide accounting state
173 MM_mode MM_register::get_limit_mode() {
174  return register_new;
175 }
176 
177 /* ************************************************************ */
178 // provide print ccounting state
179 void 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
200 size_t MM_register::memory_available() {
201  return remaining;
202 }
203 
204 /* ************************************************************ */
205 size_t MM_register::memory_used() {
206  return used;
207 }
208 
209 
210 /* ************************************************************ */
211 size_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.
224 static const size_t SIZE_SPACE=(sizeof(size_t) > 8 ? sizeof(size_t) : 8);
225 
226 
227 
228 int 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.
238 MM_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 [].
258 MM_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
285 void* MM_register::operator new[] (size_t sz) throw (std::bad_alloc) {
286 #else
287 void* 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
340 void* MM_register::operator new (size_t sz) throw (std::bad_alloc) {
341 #else
342 void* 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
396 void MM_register::operator delete (void *ptr) throw() {
397 #else
398 void 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
443 void MM_register::operator delete[] (void *ptr) throw() {
444 #else
445 void 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
487 MM_register MM_manager;
488 int MM_register::instances = 0; // Number of instances. (init)
489 // TPIE's "register memory requests" flag
490 MM_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.
501 unsigned 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.
505 mm_register_init::mm_register_init(void) {
506  if (count++ == 0) {
507  MM_manager.set_memory_limit(MM_DEFAULT_MM_SIZE);
508  }
509 }
510 
511 mm_register_init::~mm_register_init(void) {
512  --count;
513 }
MM_manager
MM_register MM_manager
Definition: mm.cpp:487
count
int count
MM_DEBUG
#define MM_DEBUG
Definition: mm.cpp:51
assert
#define assert(condition)
Definition: lz4.c:324