summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--rts/posix/OSMem.c61
-rw-r--r--rts/sm/MBlock.c2
-rw-r--r--rts/sm/OSMem.h6
-rw-r--r--rts/win32/OSMem.c4
4 files changed, 61 insertions, 12 deletions
diff --git a/rts/posix/OSMem.c b/rts/posix/OSMem.c
index 43c7831a37..274d5ada73 100644
--- a/rts/posix/OSMem.c
+++ b/rts/posix/OSMem.c
@@ -102,6 +102,7 @@ enum
MEM_RESERVE_AND_COMMIT = MEM_RESERVE | MEM_COMMIT
};
+/* Returns NULL on failure; errno set */
static void *
my_mmap (void *addr, W_ size, int operation)
{
@@ -196,6 +197,19 @@ my_mmap (void *addr, W_ size, int operation)
#endif
if (ret == (void *)-1) {
+ return NULL;
+ }
+
+ return ret;
+}
+
+/* Variant of my_mmap which aborts in the case of an error */
+static void *
+my_mmap_or_barf (void *addr, W_ size, int operation)
+{
+ void *ret = my_mmap(addr, size, operation);
+
+ if (ret == NULL) {
if (errno == ENOMEM ||
(errno == EINVAL && sizeof(void*)==4 && size >= 0xc0000000)) {
// If we request more than 3Gig, then we get EINVAL
@@ -222,7 +236,7 @@ gen_map_mblocks (W_ size)
// Try to map a larger block, and take the aligned portion from
// it (unmap the rest).
size += MBLOCK_SIZE;
- ret = my_mmap(0, size, MEM_RESERVE_AND_COMMIT);
+ ret = my_mmap_or_barf(0, size, MEM_RESERVE_AND_COMMIT);
// unmap the slop bits around the chunk we allocated
slop = (W_)ret & MBLOCK_MASK;
@@ -261,7 +275,7 @@ osGetMBlocks(nat n)
// use gen_map_mblocks the first time.
ret = gen_map_mblocks(size);
} else {
- ret = my_mmap(next_request, size, MEM_RESERVE_AND_COMMIT);
+ ret = my_mmap_or_barf(next_request, size, MEM_RESERVE_AND_COMMIT);
if (((W_)ret & MBLOCK_MASK) != 0) {
// misaligned block!
@@ -387,6 +401,9 @@ osTryReserveHeapMemory (W_ len, void *hint)
and then we discard what we don't need */
base = my_mmap(hint, len + MBLOCK_SIZE, MEM_RESERVE);
+ if (base == NULL)
+ return NULL;
+
top = (void*)((W_)base + len + MBLOCK_SIZE);
if (((W_)base & MBLOCK_MASK) != 0) {
@@ -407,7 +424,7 @@ osTryReserveHeapMemory (W_ len, void *hint)
return start;
}
-void *osReserveHeapMemory(W_ len)
+void *osReserveHeapMemory(W_ *len)
{
int attempt;
void *at;
@@ -419,15 +436,43 @@ void *osReserveHeapMemory(W_ len)
libraries are position independent but cannot be loaded about 4GB.
We do so with a hint to the mmap, and we verify the OS satisfied our
- hint. We loop a few times in case there is already something allocated
- there, but we bail if we cannot allocate at all.
+ hint. We loop, shifting our hint by 1 BLOCK_SIZE every time, in case
+ there is already something allocated there.
+
+ Some systems impose resource limits restricting the amount of memory we
+ can request (see, e.g. #10877). If mmap fails we halve our allocation
+ request and try again. If our request size gets absurdly small we simply
+ give up.
+
*/
attempt = 0;
- do {
+ while (1) {
+ if (*len < MBLOCK_SIZE) {
+ // Give up if the system won't even give us 16 blocks worth of heap
+ barf("osReserveHeapMemory: Failed to allocate heap storage");
+ }
+
void *hint = (void*)((W_)8 * (1 << 30) + attempt * BLOCK_SIZE);
- at = osTryReserveHeapMemory(len, hint);
- } while ((W_)at < ((W_)8 * (1 << 30)));
+ at = osTryReserveHeapMemory(*len, hint);
+ if (at == NULL) {
+ // This means that mmap failed which we take to mean that we asked
+ // for too much memory. This can happen due to POSIX resource
+ // limits. In this case we reduce our allocation request by a factor
+ // of two and try again.
+ *len /= 2;
+ } else if ((W_)at >= ((W_)8 * (1 << 30))) {
+ // Success! We were given a block of memory starting above the 8 GB
+ // mark, which is what we were looking for.
+ break;
+ } else {
+ // We got addressing space but it wasn't above the 8GB mark.
+ // Try again.
+ if (munmap(at, *len) < 0) {
+ sysErrorBelch("unable to release reserved heap");
+ }
+ }
+ }
return at;
}
diff --git a/rts/sm/MBlock.c b/rts/sm/MBlock.c
index e1daa71e2f..2131ae6e13 100644
--- a/rts/sm/MBlock.c
+++ b/rts/sm/MBlock.c
@@ -645,7 +645,7 @@ initMBlocks(void)
#else
size = (W_)1 << 40; // 1 TByte
#endif
- void *addr = osReserveHeapMemory(size);
+ void *addr = osReserveHeapMemory(&size);
mblock_address_space.begin = (W_)addr;
mblock_address_space.end = (W_)addr + size;
diff --git a/rts/sm/OSMem.h b/rts/sm/OSMem.h
index 6bcaf65b10..533f6f7fe6 100644
--- a/rts/sm/OSMem.h
+++ b/rts/sm/OSMem.h
@@ -34,8 +34,12 @@ void setExecutable (void *p, W_ len, rtsBool exec);
// pointed to by the return value, until that memory is committed using
// osCommitMemory().
//
+// The value pointed to by len will be filled by the caller with an upper
+// bound on the amount of memory to reserve. On return this will be set
+// to the amount of memory actually reserved.
+//
// This function is called once when the block allocator is initialized.
-void *osReserveHeapMemory(W_ len);
+void *osReserveHeapMemory(W_ *len);
// Commit (allocate memory for) a piece of address space, which must
// be within the previously reserved space After this call, it is safe
diff --git a/rts/win32/OSMem.c b/rts/win32/OSMem.c
index 2d2af0ddf6..47e24f077a 100644
--- a/rts/win32/OSMem.c
+++ b/rts/win32/OSMem.c
@@ -429,11 +429,11 @@ void setExecutable (void *p, W_ len, rtsBool exec)
static void* heap_base = NULL;
-void *osReserveHeapMemory (W_ len)
+void *osReserveHeapMemory (W_ *len)
{
void *start;
- heap_base = VirtualAlloc(NULL, len + MBLOCK_SIZE,
+ heap_base = VirtualAlloc(NULL, *len + MBLOCK_SIZE,
MEM_RESERVE, PAGE_READWRITE);
if (heap_base == NULL) {
if (GetLastError() == ERROR_NOT_ENOUGH_MEMORY) {