hurdmalloc.c 9.5 KB

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