diff options
author | Khem Raj <kraj@mvista.com> | 2008-10-11 08:52:58 +0000 |
---|---|---|
committer | Khem Raj <kraj@mvista.com> | 2008-10-11 08:52:58 +0000 |
commit | 6494060312de389feb65ad32bb411fcc64e821b7 (patch) | |
tree | b2c735bba6b667b8cf056a858dc216c14590c138 /libc/stdlib/malloc | |
parent | 47b2dbaaac9757496eb9d419e1912250354d30d1 (diff) |
Fix bug 4994 hangs on read(). I have tested the patch extensibly on ARM/LT.old.
Thank you Chase Douglas for reporting it and for the patch.
Diffstat (limited to 'libc/stdlib/malloc')
-rw-r--r-- | libc/stdlib/malloc/free.c | 22 | ||||
-rw-r--r-- | libc/stdlib/malloc/heap.h | 56 | ||||
-rw-r--r-- | libc/stdlib/malloc/heap_alloc.c | 4 | ||||
-rw-r--r-- | libc/stdlib/malloc/heap_alloc_at.c | 4 | ||||
-rw-r--r-- | libc/stdlib/malloc/heap_free.c | 4 | ||||
-rw-r--r-- | libc/stdlib/malloc/malloc.c | 20 | ||||
-rw-r--r-- | libc/stdlib/malloc/malloc.h | 6 | ||||
-rw-r--r-- | libc/stdlib/malloc/memalign.c | 2 | ||||
-rw-r--r-- | libc/stdlib/malloc/realloc.c | 12 |
9 files changed, 56 insertions, 74 deletions
diff --git a/libc/stdlib/malloc/free.c b/libc/stdlib/malloc/free.c index da395331b..fd29690ad 100644 --- a/libc/stdlib/malloc/free.c +++ b/libc/stdlib/malloc/free.c @@ -22,7 +22,7 @@ libc_hidden_proto(sbrk) #include "heap.h" static void -free_to_heap (void *mem, struct heap *heap) +free_to_heap (void *mem, struct heap_free_area *heap, malloc_mutex_t *heap_lock) { size_t size; struct heap_free_area *fa; @@ -39,7 +39,7 @@ free_to_heap (void *mem, struct heap *heap) size = MALLOC_SIZE (mem); mem = MALLOC_BASE (mem); - __heap_lock (heap); + __pthread_mutex_lock (heap_lock); /* Put MEM back in the heap, and get the free-area it was placed in. */ fa = __heap_free (heap, mem, size); @@ -48,7 +48,7 @@ free_to_heap (void *mem, struct heap *heap) unmapped. */ if (HEAP_FREE_AREA_SIZE (fa) < MALLOC_UNMAP_THRESHOLD) /* Nope, nothing left to do, just release the lock. */ - __heap_unlock (heap); + __pthread_mutex_unlock (heap_lock); else /* Yup, try to unmap FA. */ { @@ -81,7 +81,7 @@ free_to_heap (void *mem, struct heap *heap) MALLOC_DEBUG (-1, "not unmapping: 0x%lx - 0x%lx (%ld bytes)", start, end, end - start); __malloc_unlock_sbrk (); - __heap_unlock (heap); + __pthread_mutex_unlock (heap_lock); return; } #endif @@ -108,7 +108,7 @@ free_to_heap (void *mem, struct heap *heap) #ifdef MALLOC_USE_SBRK /* Release the heap lock; we're still holding the sbrk lock. */ - __heap_unlock (heap); + __pthread_mutex_unlock (heap_lock); /* Lower the brk. */ sbrk (start - end); /* Release the sbrk lock too; now we hold no locks. */ @@ -172,15 +172,15 @@ free_to_heap (void *mem, struct heap *heap) /* We have to unlock the heap before we recurse to free the mmb descriptor, because we might be unmapping from the mmb heap. */ - __heap_unlock (heap); + __pthread_mutex_unlock (heap_lock); /* Release the descriptor block we used. */ - free_to_heap (mmb, &__malloc_mmb_heap); + free_to_heap (mmb, &__malloc_mmb_heap, &__malloc_mmb_heap_lock); /* Do the actual munmap. */ munmap ((void *)mmb_start, mmb_end - mmb_start); - __heap_lock (heap); + __pthread_mutex_lock (heap_lock); # ifdef __UCLIBC_HAS_THREADS__ /* In a multi-threaded program, it's possible that PREV_MMB has @@ -213,7 +213,7 @@ free_to_heap (void *mem, struct heap *heap) } /* Finally release the lock for good. */ - __heap_unlock (heap); + __pthread_mutex_unlock (heap_lock); MALLOC_MMB_DEBUG_INDENT (-1); @@ -243,7 +243,7 @@ free_to_heap (void *mem, struct heap *heap) } /* Release the heap lock before we do the system call. */ - __heap_unlock (heap); + __pthread_mutex_unlock (heap_lock); if (unmap_end > unmap_start) /* Finally, actually unmap the memory. */ @@ -260,5 +260,5 @@ free_to_heap (void *mem, struct heap *heap) void free (void *mem) { - free_to_heap (mem, &__malloc_heap); + free_to_heap (mem, __malloc_heap, &__malloc_heap_lock); } diff --git a/libc/stdlib/malloc/heap.h b/libc/stdlib/malloc/heap.h index 6505cd223..8b05cded1 100644 --- a/libc/stdlib/malloc/heap.h +++ b/libc/stdlib/malloc/heap.h @@ -29,32 +29,12 @@ #define HEAP_GRANULARITY (__alignof__ (HEAP_GRANULARITY_TYPE)) -/* A heap is a collection of memory blocks, from which smaller blocks - of memory can be allocated. */ -struct heap -{ - /* A list of memory in the heap available for allocation. */ - struct heap_free_area *free_areas; - -#ifdef HEAP_USE_LOCKING - /* A lock that can be used by callers to control access to the heap. - The heap code _does not_ use this lock, it's merely here for the - convenience of users! */ - pthread_mutex_t lock; -#endif -}; - /* The HEAP_INIT macro can be used as a static initializer for a heap variable. The HEAP_INIT_WITH_FA variant is used to initialize a heap with an initial static free-area; its argument FA should be declared using HEAP_DECLARE_STATIC_FREE_AREA. */ -#ifdef HEAP_USE_LOCKING -# define HEAP_INIT { 0, PTHREAD_MUTEX_INITIALIZER } -# define HEAP_INIT_WITH_FA(fa) { &fa._fa, PTHREAD_MUTEX_INITIALIZER } -#else -# define HEAP_INIT { 0 } -# define HEAP_INIT_WITH_FA(fa) { &fa._fa } -#endif +# define HEAP_INIT 0 +# define HEAP_INIT_WITH_FA(fa) &fa._fa /* A free-list area `header'. These are actually stored at the _ends_ of free areas (to make allocating from the beginning of the area simpler), @@ -129,27 +109,23 @@ extern int __heap_debug; #endif /* Output a text representation of HEAP to stderr, labelling it with STR. */ -extern void __heap_dump (struct heap *heap, const char *str); +extern void __heap_dump (struct heap_free_area *heap, const char *str); /* Do some consistency checks on HEAP. If they fail, output an error message to stderr, and exit. STR is printed with the failure message. */ -extern void __heap_check (struct heap *heap, const char *str); - - -#define __heap_lock(heap) __pthread_mutex_lock (&(heap)->lock) -#define __heap_unlock(heap) __pthread_mutex_unlock (&(heap)->lock) +extern void __heap_check (struct heap_free_area *heap, const char *str); /* Delete the free-area FA from HEAP. */ static __inline__ void -__heap_delete (struct heap *heap, struct heap_free_area *fa) +__heap_delete (struct heap_free_area *heap, struct heap_free_area *fa) { if (fa->next) fa->next->prev = fa->prev; if (fa->prev) fa->prev->next = fa->next; else - heap->free_areas = fa->next; + heap = fa->next; } @@ -157,7 +133,7 @@ __heap_delete (struct heap *heap, struct heap_free_area *fa) HEAP. PREV and NEXT may be 0; if PREV is 0, FA is installed as the first free-area. */ static __inline__ void -__heap_link_free_area (struct heap *heap, struct heap_free_area *fa, +__heap_link_free_area (struct heap_free_area *heap, struct heap_free_area *fa, struct heap_free_area *prev, struct heap_free_area *next) { @@ -167,7 +143,7 @@ __heap_link_free_area (struct heap *heap, struct heap_free_area *fa, if (prev) prev->next = fa; else - heap->free_areas = fa; + heap = fa; if (next) next->prev = fa; } @@ -176,14 +152,14 @@ __heap_link_free_area (struct heap *heap, struct heap_free_area *fa, PREV may be 0, in which case FA is installed as the first free-area (but FA may not be 0). */ static __inline__ void -__heap_link_free_area_after (struct heap *heap, +__heap_link_free_area_after (struct heap_free_area *heap, struct heap_free_area *fa, struct heap_free_area *prev) { if (prev) prev->next = fa; else - heap->free_areas = fa; + heap = fa; fa->prev = prev; } @@ -192,7 +168,7 @@ __heap_link_free_area_after (struct heap *heap, PREV and NEXT may be 0; if PREV is 0, MEM is installed as the first free-area. */ static __inline__ struct heap_free_area * -__heap_add_free_area (struct heap *heap, void *mem, size_t size, +__heap_add_free_area (struct heap_free_area *heap, void *mem, size_t size, struct heap_free_area *prev, struct heap_free_area *next) { @@ -210,7 +186,7 @@ __heap_add_free_area (struct heap *heap, void *mem, size_t size, /* Allocate SIZE bytes from the front of the free-area FA in HEAP, and return the amount actually allocated (which may be more than SIZE). */ static __inline__ size_t -__heap_free_area_alloc (struct heap *heap, +__heap_free_area_alloc (struct heap_free_area *heap, struct heap_free_area *fa, size_t size) { size_t fa_size = fa->size; @@ -234,16 +210,16 @@ __heap_free_area_alloc (struct heap *heap, /* Allocate and return a block at least *SIZE bytes long from HEAP. *SIZE is adjusted to reflect the actual amount allocated (which may be greater than requested). */ -extern void *__heap_alloc (struct heap *heap, size_t *size); +extern void *__heap_alloc (struct heap_free_area *heap, size_t *size); /* Allocate SIZE bytes at address MEM in HEAP. Return the actual size allocated, or 0 if we failed. */ -extern size_t __heap_alloc_at (struct heap *heap, void *mem, size_t size); +extern size_t __heap_alloc_at (struct heap_free_area *heap, void *mem, size_t size); /* Return the memory area MEM of size SIZE to HEAP. Returns the heap free area into which the memory was placed. */ -extern struct heap_free_area *__heap_free (struct heap *heap, +extern struct heap_free_area *__heap_free (struct heap_free_area *heap, void *mem, size_t size); /* Return true if HEAP contains absolutely no memory. */ -#define __heap_is_empty(heap) (! (heap)->free_areas) +#define __heap_is_empty(heap) (! (heap)) diff --git a/libc/stdlib/malloc/heap_alloc.c b/libc/stdlib/malloc/heap_alloc.c index 9f5fd6c1a..cd52038d3 100644 --- a/libc/stdlib/malloc/heap_alloc.c +++ b/libc/stdlib/malloc/heap_alloc.c @@ -20,7 +20,7 @@ *SIZE is adjusted to reflect the actual amount allocated (which may be greater than requested). */ void * -__heap_alloc (struct heap *heap, size_t *size) +__heap_alloc (struct heap_free_area *heap, size_t *size) { struct heap_free_area *fa; size_t _size = *size; @@ -36,7 +36,7 @@ __heap_alloc (struct heap *heap, size_t *size) HEAP_DEBUG (heap, "before __heap_alloc"); /* Look for a free area that can contain _SIZE bytes. */ - for (fa = heap->free_areas; fa; fa = fa->next) + for (fa = heap; fa; fa = fa->next) if (fa->size >= _size) { /* Found one! */ diff --git a/libc/stdlib/malloc/heap_alloc_at.c b/libc/stdlib/malloc/heap_alloc_at.c index a65140fea..4c071b9ef 100644 --- a/libc/stdlib/malloc/heap_alloc_at.c +++ b/libc/stdlib/malloc/heap_alloc_at.c @@ -19,7 +19,7 @@ /* Allocate SIZE bytes at address MEM in HEAP. Return the actual size allocated, or 0 if we failed. */ size_t -__heap_alloc_at (struct heap *heap, void *mem, size_t size) +__heap_alloc_at (struct heap_free_area *heap, void *mem, size_t size) { struct heap_free_area *fa; size_t alloced = 0; @@ -29,7 +29,7 @@ __heap_alloc_at (struct heap *heap, void *mem, size_t size) HEAP_DEBUG (heap, "before __heap_alloc_at"); /* Look for a free area that can contain SIZE bytes. */ - for (fa = heap->free_areas; fa; fa = fa->next) + for (fa = heap; fa; fa = fa->next) { void *fa_mem = HEAP_FREE_AREA_START (fa); if (fa_mem <= mem) diff --git a/libc/stdlib/malloc/heap_free.c b/libc/stdlib/malloc/heap_free.c index 1c4634c55..3326bc691 100644 --- a/libc/stdlib/malloc/heap_free.c +++ b/libc/stdlib/malloc/heap_free.c @@ -18,7 +18,7 @@ /* Return the block of memory at MEM, of size SIZE, to HEAP. */ struct heap_free_area * -__heap_free (struct heap *heap, void *mem, size_t size) +__heap_free (struct heap_free_area *heap, void *mem, size_t size) { struct heap_free_area *fa, *prev_fa; void *end = (char *)mem + size; @@ -32,7 +32,7 @@ __heap_free (struct heap *heap, void *mem, size_t size) in the free-list when it becomes fragmented and long. [A better implemention would use a balanced tree or something for the free-list, though that bloats the code-size and complexity quite a bit.] */ - for (prev_fa = 0, fa = heap->free_areas; fa; prev_fa = fa, fa = fa->next) + for (prev_fa = 0, fa = heap; fa; prev_fa = fa, fa = fa->next) if (unlikely (HEAP_FREE_AREA_END (fa) >= mem)) break; diff --git a/libc/stdlib/malloc/malloc.c b/libc/stdlib/malloc/malloc.c index ce74c5608..0caf012a2 100644 --- a/libc/stdlib/malloc/malloc.c +++ b/libc/stdlib/malloc/malloc.c @@ -26,7 +26,8 @@ libc_hidden_proto(sbrk) /* The malloc heap. We provide a bit of initial static space so that programs can do a little mallocing without mmaping in more space. */ HEAP_DECLARE_STATIC_FREE_AREA (initial_fa, 256); -struct heap __malloc_heap = HEAP_INIT_WITH_FA (initial_fa); +struct heap_free_area *__malloc_heap = HEAP_INIT_WITH_FA (initial_fa); +malloc_mutex_t __malloc_heap_lock = PTHREAD_MUTEX_INITIALIZER; #if defined(MALLOC_USE_LOCKING) && defined(MALLOC_USE_SBRK) /* A lock protecting our use of sbrk. */ @@ -43,12 +44,13 @@ struct malloc_mmb *__malloc_mmapped_blocks = 0; them from the main heap, but that tends to cause heap fragmentation in annoying ways. */ HEAP_DECLARE_STATIC_FREE_AREA (initial_mmb_fa, 48); /* enough for 3 mmbs */ -struct heap __malloc_mmb_heap = HEAP_INIT_WITH_FA (initial_mmb_fa); +struct heap_free_area *__malloc_mmb_heap = HEAP_INIT_WITH_FA (initial_mmb_fa); +malloc_mutex_t __malloc_mmb_heap_lock = PTHREAD_MUTEX_INITIALIZER; #endif /* __UCLIBC_UCLINUX_BROKEN_MUNMAP__ */ static void * -malloc_from_heap (size_t size, struct heap *heap) +malloc_from_heap (size_t size, struct heap_free_area *heap, malloc_mutex_t *heap_lock) { void *mem; @@ -57,12 +59,12 @@ malloc_from_heap (size_t size, struct heap *heap) /* Include extra space to record the size of the allocated block. */ size += MALLOC_HEADER_SIZE; - __heap_lock (heap); + __pthread_mutex_lock (heap_lock); /* First try to get memory that's already in our heap. */ mem = __heap_alloc (heap, &size); - __heap_unlock (heap); + __pthread_mutex_unlock (heap_lock); if (unlikely (! mem)) /* We couldn't allocate from the heap, so grab some more @@ -126,7 +128,7 @@ malloc_from_heap (size_t size, struct heap *heap) (long)block, (long)block + block_size, block_size); /* Get back the heap lock. */ - __heap_lock (heap); + __pthread_mutex_lock (heap_lock); /* Put BLOCK into the heap. */ __heap_free (heap, block, block_size); @@ -136,7 +138,7 @@ malloc_from_heap (size_t size, struct heap *heap) /* Try again to allocate. */ mem = __heap_alloc (heap, &size); - __heap_unlock (heap); + __pthread_mutex_unlock (heap_lock); #if !defined(MALLOC_USE_SBRK) && defined(__UCLIBC_UCLINUX_BROKEN_MUNMAP__) /* Insert a record of BLOCK in sorted order into the @@ -148,7 +150,7 @@ malloc_from_heap (size_t size, struct heap *heap) if (block < mmb->mem) break; - new_mmb = malloc_from_heap (sizeof *new_mmb, &__malloc_mmb_heap); + new_mmb = malloc_from_heap (sizeof *new_mmb, __malloc_mmb_heap, &__malloc_mmb_heap_lock); new_mmb->next = mmb; new_mmb->mem = block; new_mmb->size = block_size; @@ -207,7 +209,7 @@ malloc (size_t size) if (unlikely(((unsigned long)size > (unsigned long)(MALLOC_HEADER_SIZE*-2)))) goto oom; - mem = malloc_from_heap (size, &__malloc_heap); + mem = malloc_from_heap (size, __malloc_heap, &__malloc_heap_lock); if (unlikely (!mem)) { oom: diff --git a/libc/stdlib/malloc/malloc.h b/libc/stdlib/malloc/malloc.h index 7277cd2cf..f49ed34e3 100644 --- a/libc/stdlib/malloc/malloc.h +++ b/libc/stdlib/malloc/malloc.h @@ -221,4 +221,8 @@ extern void __malloc_debug_printf (int indent, const char *fmt, ...); /* The malloc heap. */ -extern struct heap __malloc_heap; +extern struct heap_free_area *__malloc_heap; +extern malloc_mutex_t __malloc_heap_lock; +#ifdef __UCLIBC_UCLINUX_BROKEN_MUNMAP__ +extern malloc_mutex_t __malloc_mmb_heap_lock; +#endif diff --git a/libc/stdlib/malloc/memalign.c b/libc/stdlib/malloc/memalign.c index 5b248f3e2..114299b43 100644 --- a/libc/stdlib/malloc/memalign.c +++ b/libc/stdlib/malloc/memalign.c @@ -36,7 +36,7 @@ memalign (size_t alignment, size_t size) { void *mem, *base; unsigned long tot_addr, tot_end_addr, addr, end_addr; - struct heap *heap = &__malloc_heap; + struct heap_free_area *heap = __malloc_heap; /* Make SIZE something we like. */ size = HEAP_ADJUST_SIZE (size); diff --git a/libc/stdlib/malloc/realloc.c b/libc/stdlib/malloc/realloc.c index 948326762..b3b5bae14 100644 --- a/libc/stdlib/malloc/realloc.c +++ b/libc/stdlib/malloc/realloc.c @@ -59,9 +59,9 @@ realloc (void *mem, size_t new_size) { size_t extra = new_size - size; - __heap_lock (&__malloc_heap); - extra = __heap_alloc_at (&__malloc_heap, base_mem + size, extra); - __heap_unlock (&__malloc_heap); + __pthread_mutex_lock (&__malloc_heap_lock); + extra = __heap_alloc_at (__malloc_heap, base_mem + size, extra); + __pthread_mutex_unlock (&__malloc_heap_lock); if (extra) /* Record the changed size. */ @@ -82,9 +82,9 @@ realloc (void *mem, size_t new_size) else if (new_size + MALLOC_REALLOC_MIN_FREE_SIZE <= size) /* Shrink the block. */ { - __heap_lock (&__malloc_heap); - __heap_free (&__malloc_heap, base_mem + new_size, size - new_size); - __heap_unlock (&__malloc_heap); + __pthread_mutex_lock (&__malloc_heap_lock); + __heap_free (__malloc_heap, base_mem + new_size, size - new_size); + __pthread_mutex_unlock (&__malloc_heap_lock); MALLOC_SET_SIZE (base_mem, new_size); } |