GRASS GIS 7 Programmer's Manual  7.0.4(2016)-r00000
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 void* operator new[] (size_t sz) throw(std::bad_alloc) {
280  void *p;
281 
282  MM_DEBUG cout << "new: sz=" << sz << ", register "
283  << sz+SIZE_SPACE << "B ,";
284 
285  if (MM_manager.register_allocation (sz + SIZE_SPACE) != MM_ERROR_NO_ERROR){
286  //must be MM_ERROR_INSUF_SPACE
287  switch(MM_manager.register_new) {
288 
289  case MM_ABORT_ON_MEMORY_EXCEEDED:
290  cerr << "MM error: limit ="<< MM_manager.memory_limit() <<"B. "
291  << "allocating " << sz << "B. "
292  << "limit exceeded by "
293  << MM_manager.memory_used() - MM_manager.memory_limit()<<"B."
294  << endl;
295  assert (0); // core dump if debugging
296  exit (1);
297  break;
298 
299  case MM_WARN_ON_MEMORY_EXCEEDED:
300  cerr << "MM warning: limit="<<MM_manager.memory_limit() <<"B. "
301  << "allocating " << sz << "B. "
302  << " limit exceeded by "
303  << MM_manager.memory_used() - MM_manager.memory_limit()<<"B."
304  << endl;
305  break;
306 
307  case MM_IGNORE_MEMORY_EXCEEDED:
308  break;
309  }
310  }
311 
312  p = malloc(sz + SIZE_SPACE);
313 
314  if (!p) {
315  cerr << "new: out of memory while allocating " << sz << "B" << endl;
316  assert(0);
317  exit (1);
318  }
319 
320  *((size_t *) p) = sz;
321 
322  MM_DEBUG cout << "ptr=" << (void*) (((char *) p) + SIZE_SPACE) << endl;
323 
324  return ((char *) p) + SIZE_SPACE;
325 }
326 
327 
328 
329 /* ************************************************************ */
330 void* operator new (size_t sz) throw(std::bad_alloc) {
331  void *p;
332 
333  MM_DEBUG cout << "new: sz=" << sz << ", register "
334  << sz+SIZE_SPACE << "B ,";
335 
336  if (MM_manager.register_allocation (sz + SIZE_SPACE) != MM_ERROR_NO_ERROR){
337  //must be MM_ERROR_INSUF_SPACE
338  switch(MM_manager.register_new) {
339 
340  case MM_ABORT_ON_MEMORY_EXCEEDED:
341  cerr << "MM error: limit ="<< MM_manager.memory_limit() <<"B. "
342  << "allocating " << sz << "B. "
343  << "limit exceeded by "
344  << MM_manager.memory_used() - MM_manager.memory_limit()<<"B."
345  << endl;
346  assert (0); // core dump if debugging
347  exit (1);
348  break;
349 
350  case MM_WARN_ON_MEMORY_EXCEEDED:
351  cerr << "MM warning: limit="<<MM_manager.memory_limit() <<"B. "
352  << "allocating " << sz << "B. "
353  << " limit exceeded by "
354  << MM_manager.memory_used() - MM_manager.memory_limit()<<"B."
355  << endl;
356  break;
357 
358  case MM_IGNORE_MEMORY_EXCEEDED:
359  break;
360  }
361  }
362 
363  p = malloc(sz + SIZE_SPACE);
364 
365  if (!p) {
366  cerr << "new: out of memory while allocating " << sz << "B" << endl;
367  assert(0);
368  exit (1);
369  }
370 
371  *((size_t *) p) = sz;
372 
373  MM_DEBUG cout << "ptr=" << (void*) (((char *) p) + SIZE_SPACE) << endl;
374 
375  return ((char *) p) + SIZE_SPACE;
376 }
377 
378 
379 
380 
381 /* ---------------------------------------------------------------------- */
382 void operator delete (void *ptr) throw() {
383  size_t sz;
384  void *p;
385 
386  MM_DEBUG cout << "delete: ptr=" << ptr << ",";
387 
388  if (!ptr) {
389  cerr << "MM warning: operator delete was given a NULL pointer\n";
390  cerr.flush();
391  //this may actually happen: for instance when calling a default
392  //destructor for something that was not allocated with new
393  //e.g. ofstream str(name) ---- ~ofstream() called ==> ptr=NULL
394 
395  //who wrote the above comment? -RW
396  assert(0);
397  //exit(1);
398  return;
399  }
400 
401  assert(ptr);
402  p = ((char *)ptr) - SIZE_SPACE; // the base of memory
403  sz = *((size_t *)p);
404 
405  MM_DEBUG cout << "size=" << sz <<", free " << p << "B and deallocate "
406  << sz + SIZE_SPACE << endl;
407 
408  if(MM_manager.register_deallocation (sz + SIZE_SPACE) != MM_ERROR_NO_ERROR){
409  //must be MM_ERROR_UNDERFLOW
410  cerr << "delete: MM_manager.register_deallocation failed\n";
411  assert(0);
412  exit(1);
413  }
414 
415  free(p);
416 }
417 
418 
419 
420 
421 /* ---------------------------------------------------------------------- */
422 void operator delete[] (void *ptr) throw() {
423  size_t sz;
424  void *p;
425 
426  MM_DEBUG cout << "delete[]: ptr=" << ptr << ",";
427 
428  if (!ptr) {
429  //can this hapen? -- it does: see delete above
430  cerr << "MM warning: operator delete [] was given a NULL pointer\n";
431  cerr.flush();
432  //assert(0);
433  //exit(1);
434  return;
435  }
436  assert(ptr);
437  p = ((char *)ptr) - SIZE_SPACE; // the base of memory
438  sz = *((size_t *)p);
439 
440  MM_DEBUG cout << "size=" << sz <<", free " << p << "B and deallocate "
441  << sz + SIZE_SPACE << endl;
442 
443  if(MM_manager.register_deallocation (sz + SIZE_SPACE)!= MM_ERROR_NO_ERROR){
444  //must be MM_ERROR_UNDERFLOW
445  cerr << "delete[]: MM_manager.register_deallocation failed\n";
446  assert(0);
447  exit(1);
448  }
449 
450  free(p);
451 }
452 
453 
454 
455 
456 
457 /* ************************************************************ */
458 // Instantiate the actual memory manager, and allocate the
459 // its static data members
460 MM_register MM_manager;
461 int MM_register::instances = 0; // Number of instances. (init)
462 // TPIE's "register memory requests" flag
463 MM_mode MM_register::register_new = MM_IGNORE_MEMORY_EXCEEDED;
464 //This causes constructors for static variables to fail
465 //MM_mode MM_register::register_new = MM_ABORT_ON_MEMORY_EXCEEDED;
466 
467 
468 
469 
470 
471 
472 /* ************************************************************ */
473 // The counter of mm_register_init instances. It is implicity set to 0.
474 unsigned int mm_register_init::count;
475 
476 // The constructor and destructor that ensure that the memory manager is
477 // created exactly once, and destroyed when appropriate.
478 mm_register_init::mm_register_init(void) {
479  if (count++ == 0) {
480  MM_manager.set_memory_limit(MM_DEFAULT_MM_SIZE);
481  }
482 }
483 
484 mm_register_init::~mm_register_init(void) {
485  --count;
486 }
int count
#define MM_DEBUG
Definition: mm.cpp:51
MM_register MM_manager
Definition: mm.cpp:460