Changeset 19a1800 in mainline for uspace/lib/c/generic/malloc.c
- Timestamp:
- 2011-03-01T22:20:56Z (14 years ago)
- Branches:
- lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
- Children:
- e24e7b1
- Parents:
- 976f546 (diff), ac8285d (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the(diff)
links above to see all the changes relative to each parent. - File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/lib/c/generic/malloc.c
r976f546 r19a1800 45 45 #include <futex.h> 46 46 #include <adt/gcdlcm.h> 47 48 /* Magic used in heap headers. */ 49 #define HEAP_BLOCK_HEAD_MAGIC 0xBEEF0101 50 51 /* Magic used in heap footers. */ 52 #define HEAP_BLOCK_FOOT_MAGIC 0xBEEF0202 53 54 /** Allocation alignment (this also covers the alignment of fields 55 in the heap header and footer) */ 47 #include "private/malloc.h" 48 49 /** Magic used in heap headers. */ 50 #define HEAP_BLOCK_HEAD_MAGIC UINT32_C(0xBEEF0101) 51 52 /** Magic used in heap footers. */ 53 #define HEAP_BLOCK_FOOT_MAGIC UINT32_C(0xBEEF0202) 54 55 /** Magic used in heap descriptor. */ 56 #define HEAP_AREA_MAGIC UINT32_C(0xBEEFCAFE) 57 58 /** Allocation alignment. 59 * 60 * This also covers the alignment of fields 61 * in the heap header and footer. 62 * 63 */ 56 64 #define BASE_ALIGN 16 57 65 58 /** 59 * Either 4 * 256M on 32-bit architecures or 16 * 256M on 64-bit architectures 60 */ 61 #define MAX_HEAP_SIZE (sizeof(uintptr_t) << 28) 62 63 /** 64 * 65 */ 66 #define STRUCT_OVERHEAD (sizeof(heap_block_head_t) + sizeof(heap_block_foot_t)) 67 68 /** 69 * Calculate real size of a heap block (with header and footer) 66 /** Overhead of each heap block. */ 67 #define STRUCT_OVERHEAD \ 68 (sizeof(heap_block_head_t) + sizeof(heap_block_foot_t)) 69 70 /** Calculate real size of a heap block. 71 * 72 * Add header and footer size. 73 * 70 74 */ 71 75 #define GROSS_SIZE(size) ((size) + STRUCT_OVERHEAD) 72 76 73 /** 74 * Calculate net size of a heap block (without header and footer) 77 /** Calculate net size of a heap block. 78 * 79 * Subtract header and footer size. 80 * 75 81 */ 76 82 #define NET_SIZE(size) ((size) - STRUCT_OVERHEAD) 83 84 /** Get first block in heap area. 85 * 86 */ 87 #define AREA_FIRST_BLOCK(area) \ 88 (ALIGN_UP(((uintptr_t) (area)) + sizeof(heap_area_t), BASE_ALIGN)) 89 90 /** Get footer in heap block. 91 * 92 */ 93 #define BLOCK_FOOT(head) \ 94 ((heap_block_foot_t *) \ 95 (((uintptr_t) head) + head->size - sizeof(heap_block_foot_t))) 96 97 /** Heap area. 98 * 99 * The memory managed by the heap allocator is divided into 100 * multiple discontinuous heaps. Each heap is represented 101 * by a separate address space area which has this structure 102 * at its very beginning. 103 * 104 */ 105 typedef struct heap_area { 106 /** Start of the heap area (including this structure) 107 * 108 * Aligned on page boundary. 109 * 110 */ 111 void *start; 112 113 /** End of the heap area (aligned on page boundary) */ 114 void *end; 115 116 /** Next heap area */ 117 struct heap_area *next; 118 119 /** A magic value */ 120 uint32_t magic; 121 } heap_area_t; 77 122 78 123 /** Header of a heap block … … 86 131 bool free; 87 132 133 /** Heap area this block belongs to */ 134 heap_area_t *area; 135 88 136 /* A magic value to detect overwrite of heap header */ 89 137 uint32_t magic; … … 101 149 } heap_block_foot_t; 102 150 103 /** Linker heap symbol */ 104 extern char _heap; 151 /** First heap area */ 152 static heap_area_t *first_heap_area = NULL; 153 154 /** Last heap area */ 155 static heap_area_t *last_heap_area = NULL; 156 157 /** Next heap block to examine (next fit algorithm) */ 158 static heap_block_head_t *next = NULL; 105 159 106 160 /** Futex for thread-safe heap manipulation */ 107 161 static futex_t malloc_futex = FUTEX_INITIALIZER; 108 162 109 /** Address of heap start */110 static void *heap_start = 0;111 112 /** Address of heap end */113 static void *heap_end = 0;114 115 /** Maximum heap size */116 static size_t max_heap_size = (size_t) -1;117 118 /** Current number of pages of heap area */119 static size_t heap_pages = 0;120 121 163 /** Initialize a heap block 122 164 * 123 * Fill sin the structures related to a heap block.165 * Fill in the structures related to a heap block. 124 166 * Should be called only inside the critical section. 125 167 * … … 127 169 * @param size Size of the block including the header and the footer. 128 170 * @param free Indication of a free block. 129 * 130 */ 131 static void block_init(void *addr, size_t size, bool free) 171 * @param area Heap area the block belongs to. 172 * 173 */ 174 static void block_init(void *addr, size_t size, bool free, heap_area_t *area) 132 175 { 133 176 /* Calculate the position of the header and the footer */ 134 177 heap_block_head_t *head = (heap_block_head_t *) addr; 135 heap_block_foot_t *foot =136 (heap_block_foot_t *) (addr + size - sizeof(heap_block_foot_t));137 178 138 179 head->size = size; 139 180 head->free = free; 181 head->area = area; 140 182 head->magic = HEAP_BLOCK_HEAD_MAGIC; 183 184 heap_block_foot_t *foot = BLOCK_FOOT(head); 141 185 142 186 foot->size = size; … … 159 203 assert(head->magic == HEAP_BLOCK_HEAD_MAGIC); 160 204 161 heap_block_foot_t *foot = 162 (heap_block_foot_t *) (addr + head->size - sizeof(heap_block_foot_t)); 205 heap_block_foot_t *foot = BLOCK_FOOT(head); 163 206 164 207 assert(foot->magic == HEAP_BLOCK_FOOT_MAGIC); … … 166 209 } 167 210 168 /** Increase the heap area size 169 * 170 * Should be called only inside the critical section. 171 * 172 * @param size Number of bytes to grow the heap by. 173 * 174 */ 175 static bool grow_heap(size_t size) 211 /** Check a heap area structure 212 * 213 * @param addr Address of the heap area. 214 * 215 */ 216 static void area_check(void *addr) 217 { 218 heap_area_t *area = (heap_area_t *) addr; 219 220 assert(area->magic == HEAP_AREA_MAGIC); 221 assert(area->start < area->end); 222 assert(((uintptr_t) area->start % PAGE_SIZE) == 0); 223 assert(((uintptr_t) area->end % PAGE_SIZE) == 0); 224 } 225 226 /** Create new heap area 227 * 228 * @param start Preffered starting address of the new area. 229 * @param size Size of the area. 230 * 231 */ 232 static bool area_create(size_t size) 233 { 234 void *start = as_get_mappable_page(size); 235 if (start == NULL) 236 return false; 237 238 /* Align the heap area on page boundary */ 239 void *astart = (void *) ALIGN_UP((uintptr_t) start, PAGE_SIZE); 240 size_t asize = ALIGN_UP(size, PAGE_SIZE); 241 242 astart = as_area_create(astart, asize, AS_AREA_WRITE | AS_AREA_READ); 243 if (astart == (void *) -1) 244 return false; 245 246 heap_area_t *area = (heap_area_t *) astart; 247 248 area->start = astart; 249 area->end = (void *) 250 ALIGN_DOWN((uintptr_t) astart + asize, BASE_ALIGN); 251 area->next = NULL; 252 area->magic = HEAP_AREA_MAGIC; 253 254 void *block = (void *) AREA_FIRST_BLOCK(area); 255 size_t bsize = (size_t) (area->end - block); 256 257 block_init(block, bsize, true, area); 258 259 if (last_heap_area == NULL) { 260 first_heap_area = area; 261 last_heap_area = area; 262 } else { 263 last_heap_area->next = area; 264 last_heap_area = area; 265 } 266 267 return true; 268 } 269 270 /** Try to enlarge a heap area 271 * 272 * @param area Heap area to grow. 273 * @param size Gross size of item to allocate (bytes). 274 * 275 */ 276 static bool area_grow(heap_area_t *area, size_t size) 176 277 { 177 278 if (size == 0) 279 return true; 280 281 area_check(area); 282 283 size_t asize = ALIGN_UP((size_t) (area->end - area->start) + size, 284 PAGE_SIZE); 285 286 /* New heap area size */ 287 void *end = (void *) 288 ALIGN_DOWN((uintptr_t) area->start + asize, BASE_ALIGN); 289 290 /* Check for overflow */ 291 if (end < area->start) 178 292 return false; 179 180 if ((heap_start + size < heap_start) || (heap_end + size < heap_end)) 293 294 /* Resize the address space area */ 295 int ret = as_area_resize(area->start, asize, 0); 296 if (ret != EOK) 181 297 return false; 182 298 183 size_t heap_size = (size_t) (heap_end - heap_start); 184 185 if ((max_heap_size != (size_t) -1) && (heap_size + size > max_heap_size)) 186 return false; 187 188 size_t pages = (size - 1) / PAGE_SIZE + 1; 189 190 if (as_area_resize((void *) &_heap, (heap_pages + pages) * PAGE_SIZE, 0) 191 == EOK) { 192 void *end = (void *) ALIGN_DOWN(((uintptr_t) &_heap) + 193 (heap_pages + pages) * PAGE_SIZE, BASE_ALIGN); 194 block_init(heap_end, end - heap_end, true); 195 heap_pages += pages; 196 heap_end = end; 299 /* Add new free block */ 300 block_init(area->end, (size_t) (end - area->end), true, area); 301 302 /* Update heap area parameters */ 303 area->end = end; 304 305 return true; 306 } 307 308 /** Try to enlarge any of the heap areas 309 * 310 * @param size Gross size of item to allocate (bytes). 311 * 312 */ 313 static bool heap_grow(size_t size) 314 { 315 if (size == 0) 197 316 return true; 198 } 199 200 return false; 201 } 202 203 /** Decrease the heap area 204 * 205 * Should be called only inside the critical section. 206 * 207 * @param size Number of bytes to shrink the heap by. 208 * 209 */ 210 static void shrink_heap(void) 211 { 212 // TODO 317 318 /* First try to enlarge some existing area */ 319 heap_area_t *area; 320 for (area = first_heap_area; area != NULL; area = area->next) { 321 if (area_grow(area, size)) 322 return true; 323 } 324 325 /* Eventually try to create a new area */ 326 return area_create(AREA_FIRST_BLOCK(size)); 327 } 328 329 /** Try to shrink heap space 330 * 331 * In all cases the next pointer is reset. 332 * 333 */ 334 static void heap_shrink(void) 335 { 336 next = NULL; 213 337 } 214 338 215 339 /** Initialize the heap allocator 216 340 * 217 * Find how much physical memory we have and create 218 * the heap management structures that mark the whole 219 * physical memory as a single free block. 220 * 221 */ 222 void __heap_init(void) 223 { 224 futex_down(&malloc_futex); 225 226 if (as_area_create((void *) &_heap, PAGE_SIZE, 227 AS_AREA_WRITE | AS_AREA_READ)) { 228 heap_pages = 1; 229 heap_start = (void *) ALIGN_UP((uintptr_t) &_heap, BASE_ALIGN); 230 heap_end = 231 (void *) ALIGN_DOWN(((uintptr_t) &_heap) + PAGE_SIZE, BASE_ALIGN); 232 233 /* Make the entire area one large block. */ 234 block_init(heap_start, heap_end - heap_start, true); 235 } 236 237 futex_up(&malloc_futex); 238 } 239 240 /** Get maximum heap address 241 * 242 */ 243 uintptr_t get_max_heap_addr(void) 244 { 245 futex_down(&malloc_futex); 246 247 if (max_heap_size == (size_t) -1) 248 max_heap_size = 249 max((size_t) (heap_end - heap_start), MAX_HEAP_SIZE); 250 251 uintptr_t max_heap_addr = (uintptr_t) heap_start + max_heap_size; 252 253 futex_up(&malloc_futex); 254 255 return max_heap_addr; 341 * Create initial heap memory area. This routine is 342 * only called from libc initialization, thus we do not 343 * take any locks. 344 * 345 */ 346 void __malloc_init(void) 347 { 348 if (!area_create(PAGE_SIZE)) 349 abort(); 256 350 } 257 351 … … 275 369 /* Block big enough -> split. */ 276 370 void *next = ((void *) cur) + size; 277 block_init(next, cur->size - size, true );278 block_init(cur, size, false );371 block_init(next, cur->size - size, true, cur->area); 372 block_init(cur, size, false, cur->area); 279 373 } else { 280 374 /* Block too small -> use as is. */ … … 283 377 } 284 378 285 /** Allocate a memoryblock379 /** Allocate memory from heap area starting from given block 286 380 * 287 381 * Should be called only inside the critical section. 288 * 289 * @param size The size of the block to allocate.290 * @param align Memory address alignment.291 * 292 * @ return the address of the block or NULL when not enough memory.293 * 294 * /295 static void *malloc_internal(const size_t size, const size_t align) 296 { 297 if (align == 0) 298 return NULL; 299 300 size_t falign = lcm(align, BASE_ALIGN); 301 size_t real_size = GROSS_SIZE(ALIGN_UP(size, falign)); 302 303 bool grown = false; 304 void *result;305 306 loop: 307 result = NULL;308 heap_block_head_t *cur = (heap_block_head_t *) heap_start;309 310 while ((result == NULL) && ((void *) cur < heap_end)) {382 * As a side effect this function also sets the current 383 * pointer on successful allocation. 384 * 385 * @param area Heap area where to allocate from. 386 * @param first_block Starting heap block. 387 * @param final_block Heap block where to finish the search 388 * (may be NULL). 389 * @param real_size Gross number of bytes to allocate. 390 * @param falign Physical alignment of the block. 391 * 392 * @return Address of the allocated block or NULL on not enough memory. 393 * 394 */ 395 static void *malloc_area(heap_area_t *area, heap_block_head_t *first_block, 396 heap_block_head_t *final_block, size_t real_size, size_t falign) 397 { 398 area_check((void *) area); 399 assert((void *) first_block >= (void *) AREA_FIRST_BLOCK(area)); 400 assert((void *) first_block < area->end); 401 402 heap_block_head_t *cur; 403 for (cur = first_block; (void *) cur < area->end; 404 cur = (heap_block_head_t *) (((void *) cur) + cur->size)) { 311 405 block_check(cur); 406 407 /* Finish searching on the final block */ 408 if ((final_block != NULL) && (cur == final_block)) 409 break; 312 410 313 411 /* Try to find a block that is free and large enough. */ 314 412 if ((cur->free) && (cur->size >= real_size)) { 315 /* We have found a suitable block. 316 Check for alignment properties. */ 317 void *addr = ((void *) cur) + sizeof(heap_block_head_t); 318 void *aligned = (void *) ALIGN_UP(addr, falign); 413 /* 414 * We have found a suitable block. 415 * Check for alignment properties. 416 */ 417 void *addr = (void *) 418 ((uintptr_t) cur + sizeof(heap_block_head_t)); 419 void *aligned = (void *) 420 ALIGN_UP((uintptr_t) addr, falign); 319 421 320 422 if (addr == aligned) { 321 423 /* Exact block start including alignment. */ 322 424 split_mark(cur, real_size); 323 result = addr; 425 426 next = cur; 427 return addr; 324 428 } else { 325 429 /* Block start has to be aligned */ … … 327 431 328 432 if (cur->size >= real_size + excess) { 329 /* The current block is large enough to fit 330 data in including alignment */ 331 if ((void *) cur > heap_start) { 332 /* There is a block before the current block. 333 This previous block can be enlarged to compensate 334 for the alignment excess */ 335 heap_block_foot_t *prev_foot = 336 ((void *) cur) - sizeof(heap_block_foot_t); 433 /* 434 * The current block is large enough to fit 435 * data in (including alignment). 436 */ 437 if ((void *) cur > (void *) AREA_FIRST_BLOCK(area)) { 438 /* 439 * There is a block before the current block. 440 * This previous block can be enlarged to 441 * compensate for the alignment excess. 442 */ 443 heap_block_foot_t *prev_foot = (heap_block_foot_t *) 444 ((void *) cur - sizeof(heap_block_foot_t)); 337 445 338 heap_block_head_t *prev_head = 339 ( heap_block_head_t *) (((void *) cur)- prev_foot->size);446 heap_block_head_t *prev_head = (heap_block_head_t *) 447 ((void *) cur - prev_foot->size); 340 448 341 449 block_check(prev_head); … … 344 452 heap_block_head_t *next_head = ((void *) cur) + excess; 345 453 346 if ((!prev_head->free) && (excess >= STRUCT_OVERHEAD)) { 347 /* The previous block is not free and there is enough 348 space to fill in a new free block between the previous 349 and current block */ 350 block_init(cur, excess, true); 454 if ((!prev_head->free) && 455 (excess >= STRUCT_OVERHEAD)) { 456 /* 457 * The previous block is not free and there 458 * is enough free space left to fill in 459 * a new free block between the previous 460 * and current block. 461 */ 462 block_init(cur, excess, true, area); 351 463 } else { 352 /* The previous block is free (thus there is no need to 353 induce additional fragmentation to the heap) or the 354 excess is small, thus just enlarge the previous block */ 355 block_init(prev_head, prev_head->size + excess, prev_head->free); 464 /* 465 * The previous block is free (thus there 466 * is no need to induce additional 467 * fragmentation to the heap) or the 468 * excess is small. Therefore just enlarge 469 * the previous block. 470 */ 471 block_init(prev_head, prev_head->size + excess, 472 prev_head->free, area); 356 473 } 357 474 358 block_init(next_head, reduced_size, true );475 block_init(next_head, reduced_size, true, area); 359 476 split_mark(next_head, real_size); 360 result = aligned; 361 cur = next_head; 477 478 next = next_head; 479 return aligned; 362 480 } else { 363 /* The current block is the first block on the heap. 364 We have to make sure that the alignment excess 365 is large enough to fit a new free block just 366 before the current block */ 481 /* 482 * The current block is the first block 483 * in the heap area. We have to make sure 484 * that the alignment excess is large enough 485 * to fit a new free block just before the 486 * current block. 487 */ 367 488 while (excess < STRUCT_OVERHEAD) { 368 489 aligned += falign; … … 373 494 if (cur->size >= real_size + excess) { 374 495 size_t reduced_size = cur->size - excess; 375 cur = (heap_block_head_t *) (heap_start + excess); 496 cur = (heap_block_head_t *) 497 (AREA_FIRST_BLOCK(area) + excess); 376 498 377 block_init(heap_start, excess, true); 378 block_init(cur, reduced_size, true); 499 block_init((void *) AREA_FIRST_BLOCK(area), excess, 500 true, area); 501 block_init(cur, reduced_size, true, area); 379 502 split_mark(cur, real_size); 380 result = aligned; 503 504 next = cur; 505 return aligned; 381 506 } 382 507 } … … 384 509 } 385 510 } 386 387 /* Advance to the next block. */ 388 cur = (heap_block_head_t *) (((void *) cur) + cur->size); 389 } 390 391 if ((result == NULL) && (!grown)) { 392 if (grow_heap(real_size)) { 393 grown = true; 511 } 512 513 return NULL; 514 } 515 516 /** Allocate a memory block 517 * 518 * Should be called only inside the critical section. 519 * 520 * @param size The size of the block to allocate. 521 * @param align Memory address alignment. 522 * 523 * @return Address of the allocated block or NULL on not enough memory. 524 * 525 */ 526 static void *malloc_internal(const size_t size, const size_t align) 527 { 528 assert(first_heap_area != NULL); 529 530 if (align == 0) 531 return NULL; 532 533 size_t falign = lcm(align, BASE_ALIGN); 534 size_t real_size = GROSS_SIZE(ALIGN_UP(size, falign)); 535 536 bool retry = false; 537 heap_block_head_t *split; 538 539 loop: 540 541 /* Try the next fit approach */ 542 split = next; 543 544 if (split != NULL) { 545 void *addr = malloc_area(split->area, split, NULL, real_size, 546 falign); 547 548 if (addr != NULL) 549 return addr; 550 } 551 552 /* Search the entire heap */ 553 heap_area_t *area; 554 for (area = first_heap_area; area != NULL; area = area->next) { 555 heap_block_head_t *first = (heap_block_head_t *) 556 AREA_FIRST_BLOCK(area); 557 558 void *addr = malloc_area(area, first, split, real_size, 559 falign); 560 561 if (addr != NULL) 562 return addr; 563 } 564 565 if (!retry) { 566 /* Try to grow the heap space */ 567 if (heap_grow(real_size)) { 568 retry = true; 394 569 goto loop; 395 570 } 396 571 } 397 572 398 return result;573 return NULL; 399 574 } 400 575 … … 475 650 (heap_block_head_t *) (addr - sizeof(heap_block_head_t)); 476 651 477 assert((void *) head >= heap_start);478 assert((void *) head < heap_end);479 480 652 block_check(head); 481 653 assert(!head->free); 654 655 heap_area_t *area = head->area; 656 657 area_check(area); 658 assert((void *) head >= (void *) AREA_FIRST_BLOCK(area)); 659 assert((void *) head < area->end); 482 660 483 661 void *ptr = NULL; … … 489 667 /* Shrink */ 490 668 if (orig_size - real_size >= STRUCT_OVERHEAD) { 491 /* Split the original block to a full block 492 and a trailing free block */ 493 block_init((void *) head, real_size, false); 669 /* 670 * Split the original block to a full block 671 * and a trailing free block. 672 */ 673 block_init((void *) head, real_size, false, area); 494 674 block_init((void *) head + real_size, 495 orig_size - real_size, true );496 shrink_heap();675 orig_size - real_size, true, area); 676 heap_shrink(); 497 677 } 498 678 499 679 ptr = ((void *) head) + sizeof(heap_block_head_t); 500 680 } else { 501 /* Look at the next block. If it is free and the size is 502 sufficient then merge the two. Otherwise just allocate 503 a new block, copy the original data into it and 504 free the original block. */ 681 /* 682 * Look at the next block. If it is free and the size is 683 * sufficient then merge the two. Otherwise just allocate 684 * a new block, copy the original data into it and 685 * free the original block. 686 */ 505 687 heap_block_head_t *next_head = 506 688 (heap_block_head_t *) (((void *) head) + head->size); 507 689 508 if (((void *) next_head < heap_end) &&690 if (((void *) next_head < area->end) && 509 691 (head->size + next_head->size >= real_size) && 510 692 (next_head->free)) { 511 693 block_check(next_head); 512 block_init(head, head->size + next_head->size, false );694 block_init(head, head->size + next_head->size, false, area); 513 695 split_mark(head, real_size); 514 696 515 697 ptr = ((void *) head) + sizeof(heap_block_head_t); 698 next = NULL; 516 699 } else 517 700 reloc = true; … … 544 727 = (heap_block_head_t *) (addr - sizeof(heap_block_head_t)); 545 728 546 assert((void *) head >= heap_start);547 assert((void *) head < heap_end);548 549 729 block_check(head); 550 730 assert(!head->free); 731 732 heap_area_t *area = head->area; 733 734 area_check(area); 735 assert((void *) head >= (void *) AREA_FIRST_BLOCK(area)); 736 assert((void *) head < area->end); 551 737 552 738 /* Mark the block itself as free. */ … … 557 743 = (heap_block_head_t *) (((void *) head) + head->size); 558 744 559 if ((void *) next_head < heap_end) {745 if ((void *) next_head < area->end) { 560 746 block_check(next_head); 561 747 if (next_head->free) 562 block_init(head, head->size + next_head->size, true );748 block_init(head, head->size + next_head->size, true, area); 563 749 } 564 750 565 751 /* Look at the previous block. If it is free, merge the two. */ 566 if ((void *) head > heap_start) {752 if ((void *) head > (void *) AREA_FIRST_BLOCK(area)) { 567 753 heap_block_foot_t *prev_foot = 568 754 (heap_block_foot_t *) (((void *) head) - sizeof(heap_block_foot_t)); … … 574 760 575 761 if (prev_head->free) 576 block_init(prev_head, prev_head->size + head->size, true); 577 } 578 579 shrink_heap(); 762 block_init(prev_head, prev_head->size + head->size, true, 763 area); 764 } 765 766 heap_shrink(); 580 767 581 768 futex_up(&malloc_futex);
Note:
See TracChangeset
for help on using the changeset viewer.