Skip to content

Fix memory leak on Windows with isolates #2282

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.svm.core.posix;

import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.hosted.Feature;

import com.oracle.svm.core.annotate.AutomaticFeature;
import com.oracle.svm.core.os.CommittedMemoryProvider;
import com.oracle.svm.core.os.OSCommittedMemoryProvider;

@AutomaticFeature
class PosixOSCommittedMemoryProviderFeature implements Feature {
@Override
public void beforeAnalysis(BeforeAnalysisAccess access) {
if (!ImageSingletons.contains(CommittedMemoryProvider.class)) {
ImageSingletons.add(CommittedMemoryProvider.class, new OSCommittedMemoryProvider());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.svm.core.windows;

import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.StackValue;
import org.graalvm.nativeimage.c.struct.SizeOf;
import org.graalvm.nativeimage.hosted.Feature;
import org.graalvm.word.Pointer;
import org.graalvm.word.PointerBase;
import org.graalvm.word.UnsignedWord;
import org.graalvm.word.WordFactory;

import com.oracle.svm.core.annotate.AutomaticFeature;
import com.oracle.svm.core.annotate.Uninterruptible;
import com.oracle.svm.core.os.CommittedMemoryProvider;
import com.oracle.svm.core.os.OSCommittedMemoryProvider;
import com.oracle.svm.core.os.VirtualMemoryProvider;
import com.oracle.svm.core.util.PointerUtils;
import com.oracle.svm.core.windows.headers.MemoryAPI;

@AutomaticFeature
class WindowsOSCommittedMemoryProviderFeature implements Feature {
@Override
public void beforeAnalysis(BeforeAnalysisAccess access) {
if (!ImageSingletons.contains(CommittedMemoryProvider.class)) {
ImageSingletons.add(CommittedMemoryProvider.class, new WindowsOSCommittedMemoryProvider());
}
}
}

/**
* As it is not possible to free a subrange of the allocated address range on Windows, the main
* purpose of this class is to adjust how the <b>aligned</b> blocks of committed memory are
* allocated and freed to circumvent this restriction.
*/
public class WindowsOSCommittedMemoryProvider extends OSCommittedMemoryProvider {
@Override
public Pointer allocate(UnsignedWord size, UnsignedWord alignment, boolean executable) {
if (alignment.belowOrEqual(defaultAlignment())) {
return allocate(size, executable);
}

/* Reserve a container that is large enough for the requested size *and* the alignment. */
UnsignedWord containerSize = getContainerSize(size, alignment);
Pointer containerStart = VirtualMemoryProvider.get().reserve(containerSize);
if (containerStart.isNull()) {
return WordFactory.nullPointer();
}

/* Commit only the requested amount at the requested alignment within the container. */
Pointer start = PointerUtils.roundUp(containerStart, alignment);
if (VirtualMemoryProvider.get().commit(start, size, defaultProtection(executable)).isNull()) {
VirtualMemoryProvider.get().free(containerStart, containerSize);
return WordFactory.nullPointer();
}
return start;
}

@Override
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public boolean free(PointerBase start, UnsignedWord size, UnsignedWord alignment, boolean executable) {
PointerBase containerStart;
if (alignment.belowOrEqual(defaultAlignment())) {
containerStart = start;
} else {
/* Retrieve the start of the enclosing container that was originally reserved. */
MemoryAPI.MEMORY_BASIC_INFORMATION memoryInfo = StackValue.get(MemoryAPI.MEMORY_BASIC_INFORMATION.class);
MemoryAPI.VirtualQuery(start, memoryInfo, SizeOf.unsigned(MemoryAPI.MEMORY_BASIC_INFORMATION.class));
assert start.equal(memoryInfo.BaseAddress()) : "Invalid memory block start";
containerStart = memoryInfo.AllocationBase();
}
return free(containerStart, getContainerSize(size, alignment));
}

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
private static UnsignedWord getContainerSize(UnsignedWord size, UnsignedWord alignment) {
return alignment.belowOrEqual(defaultAlignment()) ? size : size.add(alignment);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,29 +30,30 @@
import org.graalvm.nativeimage.StackValue;
import org.graalvm.nativeimage.c.struct.SizeOf;
import org.graalvm.nativeimage.hosted.Feature;
import org.graalvm.word.Pointer;
import org.graalvm.word.UnsignedWord;

import com.oracle.svm.core.annotate.AutomaticFeature;
import com.oracle.svm.core.annotate.Uninterruptible;
import com.oracle.svm.core.stack.StackOverflowCheck;
import com.oracle.svm.core.windows.headers.WinBase;
import com.oracle.svm.core.windows.headers.MemoryAPI;

@Platforms({Platform.WINDOWS.class})
class WindowsStackOverflowSupport implements StackOverflowCheck.OSSupport {

@Uninterruptible(reason = "Called while thread is being attached to the VM, i.e., when the thread state is not yet set up.")
@Override
public UnsignedWord lookupStackEnd() {
WinBase.MEMORY_BASIC_INFORMATION minfo = StackValue.get(WinBase.MEMORY_BASIC_INFORMATION.class);
MemoryAPI.MEMORY_BASIC_INFORMATION minfo = StackValue.get(MemoryAPI.MEMORY_BASIC_INFORMATION.class);

/*
* We find the boundary of the stack by looking at the base of the memory block that
* contains a (random known) address of the current stack. The stack-allocated memory where
* the function result is placed in is just the easiest way to get such an address.
*/
WinBase.VirtualQuery(minfo, minfo, SizeOf.unsigned(WinBase.MEMORY_BASIC_INFORMATION.class));
MemoryAPI.VirtualQuery(minfo, minfo, SizeOf.unsigned(MemoryAPI.MEMORY_BASIC_INFORMATION.class));

return minfo.AllocationBase();
return (Pointer) minfo.AllocationBase();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.StackValue;
import org.graalvm.nativeimage.c.struct.SizeOf;
import org.graalvm.nativeimage.c.type.CIntPointer;
import org.graalvm.nativeimage.c.type.WordPointer;
import org.graalvm.nativeimage.hosted.Feature;
Expand All @@ -40,8 +41,8 @@
import com.oracle.svm.core.c.CGlobalData;
import com.oracle.svm.core.c.CGlobalDataFactory;
import com.oracle.svm.core.os.VirtualMemoryProvider;
import com.oracle.svm.core.windows.headers.MemoryAPI;
import com.oracle.svm.core.windows.headers.SysinfoAPI;
import com.oracle.svm.core.windows.headers.WinBase;

@AutomaticFeature
class WindowsVirtualMemoryProviderFeature implements Feature {
Expand Down Expand Up @@ -99,104 +100,81 @@ public UnsignedWord getAlignment() {
@Uninterruptible(reason = "May be called from uninterruptible code.", mayBeInlined = true)
private static int accessAsProt(int access) {
if (access == Access.NONE) {
return WinBase.PAGE_NOACCESS();
return MemoryAPI.PAGE_NOACCESS();
}

if ((access & Access.EXECUTE) != 0) {
if ((access & Access.READ) != 0) {
if ((access & Access.WRITE) != 0) {
return WinBase.PAGE_EXECUTE_READWRITE();
return MemoryAPI.PAGE_EXECUTE_READWRITE();
} else {
return WinBase.PAGE_EXECUTE_READ();
return MemoryAPI.PAGE_EXECUTE_READ();
}
}
if ((access & Access.WRITE) != 0) {
return WinBase.PAGE_EXECUTE_READWRITE();
return MemoryAPI.PAGE_EXECUTE_READWRITE();
}
return WinBase.PAGE_EXECUTE();
return MemoryAPI.PAGE_EXECUTE();
} else {
if ((access & Access.READ) != 0) {
if ((access & Access.WRITE) != 0) {
return WinBase.PAGE_READWRITE();
return MemoryAPI.PAGE_READWRITE();
} else {
return WinBase.PAGE_READONLY();
return MemoryAPI.PAGE_READONLY();
}
}
if ((access & Access.WRITE) != 0) {
return WinBase.PAGE_READWRITE();
return MemoryAPI.PAGE_READWRITE();
}
return WinBase.PAGE_NOACCESS();
return MemoryAPI.PAGE_NOACCESS();
}
}

@Uninterruptible(reason = "May be called from uninterruptible code.", mayBeInlined = true)
private static int accessForMap(int access) {
int prot = 0;

if ((access & Access.EXECUTE) != 0) {
prot |= WinBase.FILE_MAP_EXECUTE();
}
if ((access & Access.WRITE) != 0) {
prot |= WinBase.FILE_MAP_WRITE();
}
if ((access & Access.READ) != 0) {
prot |= WinBase.FILE_MAP_READ();
}

return prot;
}

@Override
@Uninterruptible(reason = "May be called from uninterruptible code.", mayBeInlined = true)
public Pointer reserve(UnsignedWord nbytes) {
return WinBase.VirtualAlloc(WordFactory.nullPointer(), nbytes, WinBase.MEM_RESERVE(), WinBase.PAGE_READWRITE());
return MemoryAPI.VirtualAlloc(WordFactory.nullPointer(), nbytes, MemoryAPI.MEM_RESERVE(), MemoryAPI.PAGE_NOACCESS());
}

@Override
@Uninterruptible(reason = "May be called from uninterruptible code.", mayBeInlined = true)
public Pointer mapFile(PointerBase start, UnsignedWord nbytes, WordBase fileHandle, UnsignedWord offset, int access) {
long fHandle = fileHandle.rawValue();
int prot = accessAsProt(access);
int sizeHi = (int) (nbytes.rawValue() >>> 32);
int sizeLo = (int) (nbytes.rawValue() & 0xFFFFFFFF);

Pointer fileMapping = WinBase.CreateFileMapping(fHandle, null, prot, sizeHi, sizeLo, null);
if (fileMapping.isNull()) {
return WordFactory.nullPointer();
}

int offsetHi = (int) (offset.rawValue() >>> 32);
int offsetLo = (int) (offset.rawValue() & 0xFFFFFFFF);
Pointer addr = WinBase.MapViewOfFile(fileMapping, accessForMap(access), offsetHi, offsetLo, nbytes);

return fileMapping.isNull() ? WordFactory.nullPointer() : addr;
return WordFactory.nullPointer();
}

@Override
@Uninterruptible(reason = "May be called from uninterruptible code.", mayBeInlined = true)
public Pointer commit(PointerBase start, UnsignedWord nbytes, int access) {
Pointer addr = WinBase.VirtualAlloc(start, nbytes, WinBase.MEM_COMMIT(), accessAsProt(access));
return addr.isNull() ? WordFactory.nullPointer() : addr;
return MemoryAPI.VirtualAlloc(start, nbytes, MemoryAPI.MEM_COMMIT(), accessAsProt(access));
}

@Override
@Uninterruptible(reason = "May be called from uninterruptible code.", mayBeInlined = true)
public int protect(PointerBase start, UnsignedWord nbytes, int access) {
CIntPointer oldProt = StackValue.get(CIntPointer.class);
int result = WinBase.VirtualProtect(start, nbytes, accessAsProt(access), oldProt);
int result = MemoryAPI.VirtualProtect(start, nbytes, accessAsProt(access), oldProt);
return (result != 0) ? 0 : -1;
}

@Override
@Uninterruptible(reason = "May be called from uninterruptible code.", mayBeInlined = true)
public int uncommit(PointerBase start, UnsignedWord nbytes) {
int result = WinBase.VirtualFree(start, nbytes, WinBase.MEM_DECOMMIT());
int result = MemoryAPI.VirtualFree(start, nbytes, MemoryAPI.MEM_DECOMMIT());
return (result != 0) ? 0 : -1;
}

@Override
@Uninterruptible(reason = "May be called from uninterruptible code.", mayBeInlined = true)
public int free(PointerBase start, UnsignedWord nbytes) {
return WinBase.VirtualFree(start, nbytes, WinBase.MEM_RELEASE());
assert isValid(start) : "Invalid address range start";
int result = MemoryAPI.VirtualFree(start, WordFactory.zero(), MemoryAPI.MEM_RELEASE());
return result != 0 ? 0 : -1;
}

@Uninterruptible(reason = "May be called from uninterruptible code.", mayBeInlined = true)
private static boolean isValid(PointerBase start) {
MemoryAPI.MEMORY_BASIC_INFORMATION memoryInfo = StackValue.get(MemoryAPI.MEMORY_BASIC_INFORMATION.class);
MemoryAPI.VirtualQuery(start, memoryInfo, SizeOf.unsigned(MemoryAPI.MEMORY_BASIC_INFORMATION.class));
return start.equal(memoryInfo.AllocationBase());
}
}
Loading