hurdmalloc.c 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. #include <stdlib.h>
  2. #include <string.h>
  3. #include "hurdmalloc.h" /* XXX see that file */
  4. #include <mach.h>
  5. #define vm_allocate __vm_allocate
  6. #define vm_page_size __vm_page_size
  7. /*
  8. * Mach Operating System
  9. * Copyright (c) 1991,1990,1989 Carnegie Mellon University
  10. * All Rights Reserved.
  11. *
  12. * Permission to use, copy, modify and distribute this software and its
  13. * documentation is hereby granted, provided that both the copyright
  14. * notice and this permission notice appear in all copies of the
  15. * software, derivative works or modified versions, and any portions
  16. * thereof, and that both notices appear in supporting documentation.
  17. *
  18. * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
  19. * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
  20. * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
  21. *
  22. * Carnegie Mellon requests users of this software to return to
  23. *
  24. * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
  25. * School of Computer Science
  26. * Carnegie Mellon University
  27. * Pittsburgh PA 15213-3890
  28. *
  29. * any improvements or extensions that they make and grant Carnegie Mellon
  30. * the rights to redistribute these changes.
  31. */
  32. /*
  33. * (pre-GNU) HISTORY
  34. *
  35. * Revision 2.7 91/05/14 17:57:34 mrt
  36. * Correcting copyright
  37. *
  38. * Revision 2.6 91/02/14 14:20:26 mrt
  39. * Added new Mach copyright
  40. * [91/02/13 12:41:21 mrt]
  41. *
  42. * Revision 2.5 90/11/05 14:37:33 rpd
  43. * Added malloc_fork* code.
  44. * [90/11/02 rwd]
  45. *
  46. * Add spin_lock_t.
  47. * [90/10/31 rwd]
  48. *
  49. * Revision 2.4 90/08/07 14:31:28 rpd
  50. * Removed RCS keyword nonsense.
  51. *
  52. * Revision 2.3 90/06/02 15:14:00 rpd
  53. * Converted to new IPC.
  54. * [90/03/20 20:56:57 rpd]
  55. *
  56. * Revision 2.2 89/12/08 19:53:59 rwd
  57. * Removed conditionals.
  58. * [89/10/23 rwd]
  59. *
  60. * Revision 2.1 89/08/03 17:09:46 rwd
  61. * Created.
  62. *
  63. *
  64. * 13-Sep-88 Eric Cooper (ecc) at Carnegie Mellon University
  65. * Changed realloc() to copy min(old size, new size) bytes.
  66. * Bug found by Mike Kupfer at Olivetti.
  67. */
  68. /*
  69. * File: malloc.c
  70. * Author: Eric Cooper, Carnegie Mellon University
  71. * Date: July, 1988
  72. *
  73. * Memory allocator for use with multiple threads.
  74. */
  75. #include <assert.h>
  76. #include <cthreads.h>
  77. #define MCHECK
  78. /*
  79. * Structure of memory block header.
  80. * When free, next points to next block on free list.
  81. * When allocated, fl points to free list.
  82. * Size of header is 4 bytes, so minimum usable block size is 8 bytes.
  83. */
  84. #define CHECK_BUSY 0x8a3c743e
  85. #define CHECK_FREE 0x66688b92
  86. #ifdef MCHECK
  87. typedef struct header {
  88. long check;
  89. union {
  90. struct header *next;
  91. struct free_list *fl;
  92. } u;
  93. } *header_t;
  94. #define HEADER_SIZE sizeof (struct header)
  95. #define HEADER_NEXT(h) ((h)->u.next)
  96. #define HEADER_FREE(h) ((h)->u.fl)
  97. #define HEADER_CHECK(h) ((h)->check)
  98. #define MIN_SIZE 16
  99. #define LOG2_MIN_SIZE 4
  100. #else /* ! MCHECK */
  101. typedef union header {
  102. union header *next;
  103. struct free_list *fl;
  104. } *header_t;
  105. #define HEADER_SIZE sizeof (union header)
  106. #define HEADER_NEXT(h) ((h)->next)
  107. #define HEADER_FREE(h) ((h)->fl)
  108. #define MIN_SIZE 8 /* minimum block size */
  109. #define LOG2_MIN_SIZE 3
  110. #endif /* MCHECK */
  111. typedef struct free_list {
  112. spin_lock_t lock; /* spin lock for mutual exclusion */
  113. header_t head; /* head of free list for this size */
  114. #ifdef DEBUG
  115. int in_use; /* # mallocs - # frees */
  116. #endif /* DEBUG */
  117. } *free_list_t;
  118. /*
  119. * Free list with index i contains blocks of size 2 ^ (i + LOG2_MIN_SIZE)
  120. * including header. Smallest block size is MIN_SIZE, with MIN_SIZE -
  121. * HEADER_SIZE bytes available to user. Size argument to malloc is a signed
  122. * integer for sanity checking, so largest block size is 2^31.
  123. */
  124. #define NBUCKETS 29
  125. static struct free_list malloc_free_list[NBUCKETS];
  126. /* Initialization just sets everything to zero, but might be necessary on a
  127. machine where spin_lock_init does otherwise, and is necessary when
  128. running an executable that was written by something like Emacs's unexec.
  129. It preserves the values of data variables like malloc_free_list, but
  130. does not save the vm_allocate'd space allocated by this malloc. */
  131. static void
  132. malloc_init (void)
  133. {
  134. int i;
  135. for (i = 0; i < NBUCKETS; ++i)
  136. {
  137. spin_lock_init (&malloc_free_list[i].lock);
  138. malloc_free_list[i].head = NULL;
  139. #ifdef DEBUG
  140. malloc_free_list[i].in_use = 0;
  141. #endif
  142. }
  143. /* This not only suppresses a `defined but not used' warning,
  144. but it is ABSOLUTELY NECESSARY to avoid the hyperclever
  145. compiler from "optimizing out" the entire function! */
  146. (void) &malloc_init;
  147. }
  148. static void
  149. more_memory(int size, free_list_t fl)
  150. {
  151. int amount;
  152. int n;
  153. vm_address_t where;
  154. header_t h;
  155. kern_return_t r;
  156. if (size <= vm_page_size) {
  157. amount = vm_page_size;
  158. n = vm_page_size / size;
  159. /* We lose vm_page_size - n*size bytes here. */
  160. } else {
  161. amount = size;
  162. n = 1;
  163. }
  164. r = vm_allocate(mach_task_self(), &where, (vm_size_t) amount, TRUE);
  165. assert_perror (r);
  166. h = (header_t) where;
  167. do {
  168. HEADER_NEXT (h) = fl->head;
  169. #ifdef MCHECK
  170. HEADER_CHECK (h) = CHECK_FREE;
  171. #endif
  172. fl->head = h;
  173. h = (header_t) ((char *) h + size);
  174. } while (--n != 0);
  175. }
  176. /* Declaration changed to standard one for GNU. */
  177. void *
  178. malloc (size_t size)
  179. {
  180. int i, n;
  181. free_list_t fl;
  182. header_t h;
  183. if ((int) size < 0) /* sanity check */
  184. return 0;
  185. size += HEADER_SIZE;
  186. /*
  187. * Find smallest power-of-two block size
  188. * big enough to hold requested size plus header.
  189. */
  190. i = 0;
  191. n = MIN_SIZE;
  192. while (n < size) {
  193. i += 1;
  194. n <<= 1;
  195. }
  196. ASSERT(i < NBUCKETS);
  197. fl = &malloc_free_list[i];
  198. spin_lock(&fl->lock);
  199. h = fl->head;
  200. if (h == 0) {
  201. /*
  202. * Free list is empty;
  203. * allocate more blocks.
  204. */
  205. more_memory(n, fl);
  206. h = fl->head;
  207. if (h == 0) {
  208. /*
  209. * Allocation failed.
  210. */
  211. spin_unlock(&fl->lock);
  212. return 0;
  213. }
  214. }
  215. /*
  216. * Pop block from free list.
  217. */
  218. fl->head = HEADER_NEXT (h);
  219. #ifdef MCHECK
  220. assert (HEADER_CHECK (h) == CHECK_FREE);
  221. HEADER_CHECK (h) = CHECK_BUSY;
  222. #endif
  223. #ifdef DEBUG
  224. fl->in_use += 1;
  225. #endif /* DEBUG */
  226. spin_unlock(&fl->lock);
  227. /*
  228. * Store free list pointer in block header
  229. * so we can figure out where it goes
  230. * at free() time.
  231. */
  232. HEADER_FREE (h) = fl;
  233. /*
  234. * Return pointer past the block header.
  235. */
  236. return ((char *) h) + HEADER_SIZE;
  237. }
  238. /* Declaration changed to standard one for GNU. */
  239. void
  240. free (void *base)
  241. {
  242. header_t h;
  243. free_list_t fl;
  244. int i;
  245. if (base == 0)
  246. return;
  247. /*
  248. * Find free list for block.
  249. */
  250. h = (header_t) (base - HEADER_SIZE);
  251. #ifdef MCHECK
  252. assert (HEADER_CHECK (h) == CHECK_BUSY);
  253. #endif
  254. fl = HEADER_FREE (h);
  255. i = fl - malloc_free_list;
  256. /*
  257. * Sanity checks.
  258. */
  259. if (i < 0 || i >= NBUCKETS) {
  260. ASSERT(0 <= i && i < NBUCKETS);
  261. return;
  262. }
  263. if (fl != &malloc_free_list[i]) {
  264. ASSERT(fl == &malloc_free_list[i]);
  265. return;
  266. }
  267. /*
  268. * Push block on free list.
  269. */
  270. spin_lock(&fl->lock);
  271. HEADER_NEXT (h) = fl->head;
  272. #ifdef MCHECK
  273. HEADER_CHECK (h) = CHECK_FREE;
  274. #endif
  275. fl->head = h;
  276. #ifdef DEBUG
  277. fl->in_use -= 1;
  278. #endif /* DEBUG */
  279. spin_unlock(&fl->lock);
  280. return;
  281. }
  282. /* Declaration changed to standard one for GNU. */
  283. void *
  284. realloc (void *old_base, size_t new_size)
  285. {
  286. header_t h;
  287. free_list_t fl;
  288. int i;
  289. unsigned int old_size;
  290. char *new_base;
  291. if (old_base == 0)
  292. return malloc (new_size);
  293. /*
  294. * Find size of old block.
  295. */
  296. h = (header_t) (old_base - HEADER_SIZE);
  297. #ifdef MCHECK
  298. assert (HEADER_CHECK (h) == CHECK_BUSY);
  299. #endif
  300. fl = HEADER_FREE (h);
  301. i = fl - malloc_free_list;
  302. /*
  303. * Sanity checks.
  304. */
  305. if (i < 0 || i >= NBUCKETS) {
  306. ASSERT(0 <= i && i < NBUCKETS);
  307. return 0;
  308. }
  309. if (fl != &malloc_free_list[i]) {
  310. ASSERT(fl == &malloc_free_list[i]);
  311. return 0;
  312. }
  313. /*
  314. * Free list with index i contains blocks of size
  315. * 2 ^ (i + * LOG2_MIN_SIZE) including header.
  316. */
  317. old_size = (1 << (i + LOG2_MIN_SIZE)) - HEADER_SIZE;
  318. if (new_size <= old_size
  319. && new_size > (((old_size + HEADER_SIZE) >> 1) - HEADER_SIZE))
  320. /* The new size still fits in the same block, and wouldn't fit in
  321. the next smaller block! */
  322. return old_base;
  323. /*
  324. * Allocate new block, copy old bytes, and free old block.
  325. */
  326. new_base = malloc(new_size);
  327. if (new_base)
  328. memcpy (new_base, old_base,
  329. (int) (old_size < new_size ? old_size : new_size));
  330. if (new_base || new_size == 0)
  331. /* Free OLD_BASE, but only if the malloc didn't fail. */
  332. free (old_base);
  333. return new_base;
  334. }
  335. #ifdef DEBUG
  336. void
  337. print_malloc_free_list (void)
  338. {
  339. int i, size;
  340. free_list_t fl;
  341. int n;
  342. header_t h;
  343. int total_used = 0;
  344. int total_free = 0;
  345. fprintf(stderr, " Size In Use Free Total\n");
  346. for (i = 0, size = MIN_SIZE, fl = malloc_free_list;
  347. i < NBUCKETS;
  348. i += 1, size <<= 1, fl += 1) {
  349. spin_lock(&fl->lock);
  350. if (fl->in_use != 0 || fl->head != 0) {
  351. total_used += fl->in_use * size;
  352. for (n = 0, h = fl->head; h != 0; h = HEADER_NEXT (h), n += 1)
  353. ;
  354. total_free += n * size;
  355. fprintf(stderr, "%10d %10d %10d %10d\n",
  356. size, fl->in_use, n, fl->in_use + n);
  357. }
  358. spin_unlock(&fl->lock);
  359. }
  360. fprintf(stderr, " all sizes %10d %10d %10d\n",
  361. total_used, total_free, total_used + total_free);
  362. }
  363. #endif /* DEBUG */
  364. void
  365. _hurd_malloc_fork_prepare(void)
  366. /*
  367. * Prepare the malloc module for a fork by insuring that no thread is in a
  368. * malloc critical section.
  369. */
  370. {
  371. int i;
  372. for (i = 0; i < NBUCKETS; i++) {
  373. spin_lock(&malloc_free_list[i].lock);
  374. }
  375. }
  376. void
  377. _hurd_malloc_fork_parent(void)
  378. /*
  379. * Called in the parent process after a fork() to resume normal operation.
  380. */
  381. {
  382. int i;
  383. for (i = NBUCKETS-1; i >= 0; i--) {
  384. spin_unlock(&malloc_free_list[i].lock);
  385. }
  386. }
  387. void
  388. _hurd_malloc_fork_child(void)
  389. /*
  390. * Called in the child process after a fork() to resume normal operation.
  391. */
  392. {
  393. int i;
  394. for (i = NBUCKETS-1; i >= 0; i--) {
  395. spin_unlock(&malloc_free_list[i].lock);
  396. }
  397. }
  398. text_set_element (_hurd_preinit_hook, malloc_init);