From 30b8af98c0d9ab172842feebf38d1a7ef00a6afa Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 13 May 2014 01:09:38 -0400 Subject: [PATCH] runtime: handle decommit failure gracefully on Windows I have no test case for this at tip. The original report included a program crashing at revision 88ac7297d2fa. I tested this code at that revision and it does fix the crash. However, at tip the reported code no longer crashes, presumably because some allocation patterns have changed. I believe the bug is still present at tip and that this code still fixes it. Fixes #7143. LGTM=alex.brainman R=golang-codereviews, alex.brainman CC=dvyukov, golang-codereviews https://golang.org/cl/96300046 --- src/pkg/runtime/mem_windows.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/pkg/runtime/mem_windows.c b/src/pkg/runtime/mem_windows.c index 7f55677c29..77ec6e9262 100644 --- a/src/pkg/runtime/mem_windows.c +++ b/src/pkg/runtime/mem_windows.c @@ -36,10 +36,30 @@ void runtime·SysUnused(void *v, uintptr n) { void *r; + uintptr small; r = runtime·stdcall(runtime·VirtualFree, 3, v, n, (uintptr)MEM_DECOMMIT); - if(r == nil) - runtime·throw("runtime: failed to decommit pages"); + if(r != nil) + return; + + // Decommit failed. Usual reason is that we've merged memory from two different + // VirtualAlloc calls, and Windows will only let each VirtualFree handle pages from + // a single VirtualAlloc. It is okay to specify a subset of the pages from a single alloc, + // just not pages from multiple allocs. This is a rare case, arising only when we're + // trying to give memory back to the operating system, which happens on a time + // scale of minutes. It doesn't have to be terribly fast. Instead of extra bookkeeping + // on all our VirtualAlloc calls, try freeing successively smaller pieces until + // we manage to free something, and then repeat. This ends up being O(n log n) + // in the worst case, but that's fast enough. + while(n > 0) { + small = n; + while(small >= 4096 && runtime·stdcall(runtime·VirtualFree, 3, v, small, (uintptr)MEM_DECOMMIT) == nil) + small = (small / 2) & ~(4096-1); + if(small < 4096) + runtime·throw("runtime: failed to decommit pages"); + v = (byte*)v + small; + n -= small; + } } void -- 2.50.0