diff options
author | Corinna Vinschen <corinna@vinschen.de> | 2013-08-30 20:01:10 +0000 |
---|---|---|
committer | Corinna Vinschen <corinna@vinschen.de> | 2013-08-30 20:01:10 +0000 |
commit | b03bd1f41c334a739c343b3d6574125752a8ad12 (patch) | |
tree | 843f12297af8b27cf40e48f1cc08da6da31b6106 | |
parent | a723366660c9f9282d9c73db59e4e64ef051accb (diff) | |
download | newlib-b03bd1f41c334a739c343b3d6574125752a8ad12.zip newlib-b03bd1f41c334a739c343b3d6574125752a8ad12.tar.gz newlib-b03bd1f41c334a739c343b3d6574125752a8ad12.tar.bz2 |
* heap.cc (sbrk): Add a FIXME comment to VirtualFree call. Fix memory
reservation and commit strategy when more memory is requested than
available on the heap. Release newly reserved memory if commiting
it fails. Add more comments to explain what we do.
-rw-r--r-- | winsup/cygwin/heap.cc | 61 |
1 files changed, 43 insertions, 18 deletions
diff --git a/winsup/cygwin/heap.cc b/winsup/cygwin/heap.cc index b10261c..b8f72bd 100644 --- a/winsup/cygwin/heap.cc +++ b/winsup/cygwin/heap.cc @@ -245,7 +245,7 @@ extern "C" void * sbrk (ptrdiff_t n) { char *newtop, *newbrk; - SIZE_T commitbytes, newbrksize; + SIZE_T commitbytes, newbrksize, reservebytes; if (n == 0) return cygheap->user_heap.ptr; /* Just wanted to find current cygheap->user_heap.ptr address */ @@ -261,6 +261,9 @@ sbrk (ptrdiff_t n) { /* Freeing memory */ assert (newtop < cygheap->user_heap.top); n = (char *) cygheap->user_heap.top - newtop; + /* FIXME: This doesn't work if we cross a virtual memory reservation + border. If that happens, we have to free the space in multiple + VirtualFree calls, aligned to the former reservation borders. */ if (VirtualFree (newtop, n, MEM_DECOMMIT)) /* Give it back to OS */ goto good; goto err; /* Didn't take */ @@ -277,27 +280,49 @@ sbrk (ptrdiff_t n) reserved. */ if (newtop <= cygheap->user_heap.max) { - if (VirtualAlloc (cygheap->user_heap.top, commitbytes, MEM_COMMIT, PAGE_READWRITE) != NULL) + if (VirtualAlloc (cygheap->user_heap.top, commitbytes, + MEM_COMMIT, PAGE_READWRITE) != NULL) goto good; } - /* Couldn't allocate memory. Maybe we can reserve some more. - Reserve either the maximum of the standard cygwin_shared->heap_chunk_size () + /* The remainder of the existing heap is too small to fulfil the memory + request. We have to extend the heap, so we reserve some more memory + and then commit the remainder of the old heap, if any, and the rest of + the required space from the extended heap. */ + + /* Reserve either the maximum of the standard heap chunk size or the requested amount. Then attempt to actually allocate it. */ - if ((newbrksize = cygheap->user_heap.chunk) < commitbytes) - newbrksize = commitbytes; - - if ((VirtualAlloc (cygheap->user_heap.top, newbrksize, - MEM_RESERVE, PAGE_NOACCESS) - || VirtualAlloc (cygheap->user_heap.top, newbrksize = commitbytes, - MEM_RESERVE, PAGE_NOACCESS)) - && VirtualAlloc (cygheap->user_heap.top, commitbytes, - MEM_COMMIT, PAGE_READWRITE) != NULL) - { - cygheap->user_heap.max = (char *) cygheap->user_heap.max - + pround (newbrksize); - goto good; - } + reservebytes = commitbytes - ((ptrdiff_t) cygheap->user_heap.max + - (ptrdiff_t) cygheap->user_heap.top); + commitbytes -= reservebytes; + if ((newbrksize = cygheap->user_heap.chunk) < reservebytes) + newbrksize = reservebytes; + + if (VirtualAlloc (cygheap->user_heap.max, newbrksize, + MEM_RESERVE, PAGE_NOACCESS) + || VirtualAlloc (cygheap->user_heap.max, newbrksize = reservebytes, + MEM_RESERVE, PAGE_NOACCESS)) + { + /* Now commit the requested memory. Windows keeps all virtual + reservations separate, so we can't commit the two regions in a single, + combined call or we suffer an ERROR_INVALID_ADDRESS. The same error + is returned when trying to VirtualAlloc 0 bytes, which would occur if + the existing heap was already full. */ + if ((!commitbytes || VirtualAlloc (cygheap->user_heap.top, commitbytes, + MEM_COMMIT, PAGE_READWRITE)) + && VirtualAlloc (cygheap->user_heap.max, reservebytes, + MEM_COMMIT, PAGE_READWRITE)) + { + cygheap->user_heap.max = (char *) cygheap->user_heap.max + + pround (newbrksize); + goto good; + } + /* If committing the memory failed, we must free the extendend reserved + region, otherwise any other try to fetch memory (for instance by using + mmap) may fail just because we still reserve memory we don't even know + about. */ + VirtualFree (cygheap->user_heap.max, newbrksize, MEM_RELEASE); + } err: set_errno (ENOMEM); |