diff --git a/src/mono/browser/runtime/memory.ts b/src/mono/browser/runtime/memory.ts index 52b5b581a5bd71..1834bcf3b88891 100644 --- a/src/mono/browser/runtime/memory.ts +++ b/src/mono/browser/runtime/memory.ts @@ -8,7 +8,7 @@ import { VoidPtr, CharPtr } from "./types/emscripten"; import cwraps, { I52Error } from "./cwraps"; import { Module, mono_assert, runtimeHelpers } from "./globals"; import { utf8ToString } from "./strings"; -import { mono_log_warn } from "./logging"; +import { mono_log_warn, mono_log_error } from "./logging"; const alloca_stack: Array = []; const alloca_buffer_size = 32 * 1024; @@ -327,6 +327,10 @@ export function withStackAlloc (bytesWanted: number, f: (pt export function mono_wasm_load_bytes_into_heap (bytes: Uint8Array): VoidPtr { // pad sizes by 16 bytes for simd const memoryOffset = Module._malloc(bytes.length + 16); + if (memoryOffset <= 0) { + mono_log_error(`malloc failed to allocate ${(bytes.length + 16)} bytes.`); + throw new Error("Out of memory"); + } const heapBytes = new Uint8Array(localHeapViewU8().buffer, memoryOffset, bytes.length); heapBytes.set(bytes); return memoryOffset; @@ -338,11 +342,20 @@ export function mono_wasm_load_bytes_into_heap (bytes: Uint8Array): VoidPtr { export function mono_wasm_load_bytes_into_heap_persistent (bytes: Uint8Array): VoidPtr { // pad sizes by 16 bytes for simd const desiredSize = bytes.length + 16; - // wasm memory page size is 64kb. allocations smaller than that are probably best - // serviced by malloc - const memoryOffset = (desiredSize < (64 * 1024)) - ? Module._malloc(desiredSize) - : Module._sbrk(desiredSize); + // sbrk doesn't allocate whole pages so we can ask it for as many bytes as we want. + let memoryOffset = Module._sbrk(desiredSize); + if (memoryOffset <= 0) { + // sbrk failed. Due to identical bugs in v8 and spidermonkey, this isn't guaranteed to be OOM. + // We use this function early during startup, when OOM shouldn't be possible anyway! + // Log a warning, then retry. + memoryOffset = Module._sbrk(desiredSize); + if (memoryOffset <= 0) { + mono_log_error(`sbrk failed to allocate ${desiredSize} bytes, and failed upon retry.`); + throw new Error("Out of memory"); + } else { + mono_log_warn(`sbrk failed to allocate ${desiredSize} bytes, but succeeded upon retry!`); + } + } const heapBytes = new Uint8Array(localHeapViewU8().buffer, memoryOffset, bytes.length); heapBytes.set(bytes); return memoryOffset; diff --git a/src/mono/mono/utils/mono-wasm-pagemgr.c b/src/mono/mono/utils/mono-wasm-pagemgr.c index 17f7dd4c6d63c3..d3a25a0ec4b851 100644 --- a/src/mono/mono/utils/mono-wasm-pagemgr.c +++ b/src/mono/mono/utils/mono-wasm-pagemgr.c @@ -237,16 +237,24 @@ acquire_new_pages_initialized (uint32_t page_count) { // We know that on WASM, sbrk grows the heap as necessary in order to return, // a region of N zeroed bytes, which isn't necessarily aligned or page-sized - uint8_t *allocation = sbrk ((uint32_t)bytes), - *allocation_end = allocation + bytes; + uint8_t *allocation = sbrk ((uint32_t)bytes); if (allocation == (uint8_t *)-1) { + // HACK: It is theoretically possible for sbrk to fail in a non-OOM condition + // due to identical bugs in v8 and spidermonkey, so retry exactly once. + allocation = sbrk ((uint32_t)bytes); + if (allocation == (uint8_t *)-1) { #ifdef MWPM_LOGGING - g_print ("mwpm failed to acquire memory\n"); + g_print ("mwpm failed to acquire memory\n"); #endif - return NULL; + return NULL; + } else { + g_print ("MWPM WARNING: sbrk() failed once, then succeeded. Continuing.\n"); + } } + uint8_t *allocation_end = allocation + bytes; + g_assert (allocation_end != allocation); // If nobody else has called sbrk since we did, stitch the allocations together