mm.cpp 13 KB

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