]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: handle decommit failure gracefully on Windows
authorRuss Cox <rsc@golang.org>
Tue, 13 May 2014 05:09:38 +0000 (01:09 -0400)
committerRuss Cox <rsc@golang.org>
Tue, 13 May 2014 05:09:38 +0000 (01:09 -0400)
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

index 7f55677c29e04b938b024dc4ec9692a46112ec82..77ec6e92626abdb5cc7696817ae1d2eac5191a0d 100644 (file)
@@ -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