--- old/make/autoconf/hotspot.m4 2019-10-31 09:39:40.648976519 +0100 +++ new/make/autoconf/hotspot.m4 2019-10-31 09:39:40.200969353 +0100 @@ -351,6 +351,7 @@ # Only enable ZGC on supported platforms AC_MSG_CHECKING([if zgc can be built]) if (test "x$OPENJDK_TARGET_OS" = "xlinux" && test "x$OPENJDK_TARGET_CPU" = "xx86_64") || \ + (test "x$OPENJDK_TARGET_OS" = "xwindows" && test "x$OPENJDK_TARGET_CPU" = "xx86_64") || \ (test "x$OPENJDK_TARGET_OS" = "xlinux" && test "x$OPENJDK_TARGET_CPU" = "xaarch64"); then AC_MSG_RESULT([yes]) else --- old/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp 2019-10-31 09:39:41.232985861 +0100 +++ new/src/hotspot/cpu/x86/gc/z/zBarrierSetAssembler_x86.cpp 2019-10-31 09:39:40.816979206 +0100 @@ -517,8 +517,11 @@ // Sort by size, largest first _xmm_registers.sort(xmm_compare_register_size); + // On Windows, the caller reserves stack space for spilling register arguments + const int arg_spill_size = frame::arg_reg_save_area_bytes; + // Stack pointer must be 16 bytes aligned for the call - _spill_offset = _spill_size = align_up(xmm_spill_size + gp_spill_size, 16); + _spill_offset = _spill_size = align_up(xmm_spill_size + gp_spill_size + arg_spill_size, 16); } public: --- /dev/null 2019-10-01 11:37:31.106015611 +0200 +++ new/src/hotspot/os/windows/gc/z/zBackingFile_windows.cpp 2019-10-31 09:39:41.412988740 +0100 @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2019, 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. + * + * 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. + */ + +#include "precompiled.hpp" +#include "gc/z/zBackingFile_windows.hpp" +#include "gc/z/zGlobals.hpp" +#include "gc/z/zGranuleMap.inline.hpp" +#include "gc/z/zMapper_windows.hpp" +#include "logging/log.hpp" +#include "runtime/globals.hpp" +#include "utilities/debug.hpp" + +// The backing file commits and uncommits physical memory, that can be +// multi-mapped into the virtual address space. To support fine-graned +// committing and uncommitting, each ZGranuleSize chunked is mapped to +// a separate paging file mapping. + +ZBackingFile::ZBackingFile() : + _handles(MaxHeapSize), + _size(0) {} + +size_t ZBackingFile::size() const { + return _size; +} + +HANDLE ZBackingFile::get_handle(uintptr_t offset) const { + HANDLE const handle = _handles.get(offset); + assert(handle != 0, "Should be set"); + return handle; +} + +void ZBackingFile::put_handle(uintptr_t offset, HANDLE handle) { + assert(handle != INVALID_HANDLE_VALUE, "Invalid handle"); + assert(_handles.get(offset) == 0, "Should be cleared"); + _handles.put(offset, handle); +} + +void ZBackingFile::clear_handle(uintptr_t offset) { + assert(_handles.get(offset) != 0, "Should be set"); + _handles.put(offset, 0); +} + +size_t ZBackingFile::commit_from_paging_file(size_t offset, size_t size) { + for (size_t i = 0; i < size; i += ZGranuleSize) { + HANDLE const handle = ZMapper::create_and_commit_paging_file_mapping(ZGranuleSize); + if (handle == 0) { + return i; + } + + put_handle(offset + i, handle); + } + + return size; +} + +size_t ZBackingFile::uncommit_from_paging_file(size_t offset, size_t size) { + for (size_t i = 0; i < size; i += ZGranuleSize) { + HANDLE const handle = get_handle(offset + i); + clear_handle(offset + i); + ZMapper::close_paging_file_mapping(handle); + } + + return size; +} + +size_t ZBackingFile::commit(size_t offset, size_t length) { + log_trace(gc, heap)("Committing memory: " SIZE_FORMAT "M-" SIZE_FORMAT "M (" SIZE_FORMAT "M)", + offset / M, (offset + length) / M, length / M); + + const size_t committed = commit_from_paging_file(offset, length); + + const size_t end = offset + committed; + if (end > _size) { + // Update size + _size = end; + } + + return committed; +} + +size_t ZBackingFile::uncommit(size_t offset, size_t length) { + log_trace(gc, heap)("Uncommitting memory: " SIZE_FORMAT "M-" SIZE_FORMAT "M (" SIZE_FORMAT "M)", + offset / M, (offset + length) / M, length / M); + + return uncommit_from_paging_file(offset, length); +} + +void ZBackingFile::map(uintptr_t addr, size_t size, size_t offset) const { + assert(is_aligned(offset, ZGranuleSize), "Misaligned"); + assert(is_aligned(addr, ZGranuleSize), "Misaligned"); + assert(is_aligned(size, ZGranuleSize), "Misaligned"); + + for (size_t i = 0; i < size; i += ZGranuleSize) { + HANDLE const handle = get_handle(offset + i); + ZMapper::map_view_replace_placeholder(handle, 0 /* offset */, addr + i, ZGranuleSize); + } +} + +void ZBackingFile::unmap(uintptr_t addr, size_t size) const { + assert(is_aligned(addr, ZGranuleSize), "Misaligned"); + assert(is_aligned(size, ZGranuleSize), "Misaligned"); + + for (size_t i = 0; i < size; i += ZGranuleSize) { + ZMapper::unmap_view_preserve_placeholder(addr + i, ZGranuleSize); + } +} --- /dev/null 2019-10-01 11:37:31.106015611 +0200 +++ new/src/hotspot/os/windows/gc/z/zBackingFile_windows.hpp 2019-10-31 09:39:42.133000256 +0100 @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019, 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. + * + * 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. + */ + +#ifndef OS_WINDOWS_GC_Z_ZBACKINGFILE_WINDOWS_HPP +#define OS_WINDOWS_GC_Z_ZBACKINGFILE_WINDOWS_HPP + +#include "gc/z/zGranuleMap.hpp" +#include "memory/allocation.hpp" + +#include + +class ZBackingFile { +private: + ZGranuleMap _handles; + size_t _size; + + HANDLE get_handle(uintptr_t offset) const; + void put_handle(uintptr_t offset, HANDLE handle); + void clear_handle(uintptr_t offset); + + size_t commit_from_paging_file(size_t offset, size_t size); + size_t uncommit_from_paging_file(size_t offset, size_t size); + +public: + ZBackingFile(); + + size_t size() const; + + size_t commit(size_t offset, size_t length); + size_t uncommit(size_t offset, size_t length); + + void map(uintptr_t addr, size_t size, size_t offset) const; + void unmap(uintptr_t addr, size_t size) const; +}; + +#endif // OS_WINDOWS_GC_Z_ZBACKINGFILE_WINDOWS_HPP --- /dev/null 2019-10-01 11:37:31.106015611 +0200 +++ new/src/hotspot/os/windows/gc/z/zInitialize_windows.cpp 2019-10-31 09:39:42.789010750 +0100 @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2019, 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. + * + * 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. + */ + +#include "precompiled.hpp" +#include "gc/z/zInitialize.hpp" +#include "gc/z/zSyscall_windows.hpp" + +void ZInitialize::initialize_os() { + ZSyscall::initialize(); +} --- /dev/null 2019-10-01 11:37:31.106015611 +0200 +++ new/src/hotspot/os/windows/gc/z/zLargePages_windows.cpp 2019-10-31 09:39:43.389020348 +0100 @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2019, 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. + * + * 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. + */ + +#include "precompiled.hpp" +#include "gc/z/zLargePages.hpp" + +void ZLargePages::initialize_platform() { + _state = Disabled; +} --- /dev/null 2019-10-01 11:37:31.106015611 +0200 +++ new/src/hotspot/os/windows/gc/z/zMapper_windows.cpp 2019-10-31 09:39:44.037030713 +0100 @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2019, 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. + * + * 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. + */ + +#include "precompiled.hpp" +#include "gc/z/zMapper_windows.hpp" +#include "gc/z/zSyscall_windows.hpp" +#include "logging/log.hpp" +#include "utilities/debug.hpp" + +#include + +// Memory reservation, commit, views, and placeholders. +// +// To be able to up-front reserve address space for the heap views, and later +// multi-map the heap views to the same physical memory, without ever losing the +// reservation of the reserved address space, we use "placeholders". +// +// These placeholders block out the address space from being used by other parts +// of the process. To commit memory in this address space, the placeholder must +// be replaced by anonymous memory, or replaced by mapping a view against a +// paging file mapping. We use the later to support multi-mapping. +// +// We want to be able to dynamically commit and uncommit the physical memory of +// the heap (and also unmap ZPages), in granules of ZGranuleSize bytes. There is +// no way to grow and shrink the committed memory of a paging file mapping. +// Therefore, we create multiple granule-sized page file mappings. The memory is +// committed by creating a page file mapping, map a view against it, commit the +// memory, unmap the view. The memory will stay committed until all views are +// unmapped, and the paging file mapping handle is closed. +// +// When replacing a placeholder address space reservation with a mapped view +// against a paging file mapping, the virtual address space must exactly match +// an existing placeholder's address and size. Therefore we only deal with +// granule-sized placeholders at this layer. Higher layers that keep track of +// reserved available address space can (and will) coalesce placeholders, but +// they will be split before being used. + +#define fatal_error(msg, addr, size) \ + fatal(msg ": " PTR_FORMAT " " SIZE_FORMAT "M (%d)", \ + (addr), (size) / M, GetLastError()) + +uintptr_t ZMapper::reserve(uintptr_t addr, size_t size) { + void* const res = ZSyscall::VirtualAlloc2( + GetCurrentProcess(), // Process + (void*)addr, // BaseAddress + size, // Size + MEM_RESERVE | MEM_RESERVE_PLACEHOLDER, // AllocationType + PAGE_NOACCESS, // PageProtection + NULL, // ExtendedParameters + 0 // ParameterCount + ); + + // Caller responsible for error handling + return (uintptr_t)res; +} + +void ZMapper::unreserve(uintptr_t addr, size_t size) { + const bool res = ZSyscall::VirtualFreeEx( + GetCurrentProcess(), // hProcess + (void*)addr, // lpAddress + size, // dwSize + MEM_RELEASE // dwFreeType + ); + + if (!res) { + fatal_error("Failed to unreserve memory", addr, size); + } +} + +HANDLE ZMapper::create_paging_file_mapping(size_t size) { + // Create mapping with SEC_RESERVE instead of SEC_COMMIT. + // + // We use MapViewOfFile3 for two different reasons: + // 1) When commiting memory for the created paging file + // 2) When mapping a view of the memory created in (2) + // + // The non-platform code is only setup to deal with out-of-memory + // errors in (1). By using SEC_RESERVE, we prevent MapViewOfFile3 + // from failing because of "commit limit" checks. To actually commit + // memory in (1), a call to VirtualAlloc2 is done. + + HANDLE const res = ZSyscall::CreateFileMappingW( + INVALID_HANDLE_VALUE, // hFile + NULL, // lpFileMappingAttribute + PAGE_READWRITE | SEC_RESERVE, // flProtect + size >> 32, // dwMaximumSizeHigh + size & 0xFFFFFFFF, // dwMaximumSizeLow + NULL // lpName + ); + + // Caller responsible for error handling + return res; +} + +bool ZMapper::commit_paging_file_mapping(HANDLE file_handle, uintptr_t file_offset, size_t size) { + const uintptr_t addr = map_view_no_placeholder(file_handle, file_offset, size); + if (addr == 0) { + log_error(gc)("Failed to map view of paging file mapping (%d)", GetLastError()); + return false; + } + + const uintptr_t res = commit(addr, size); + if (res != addr) { + log_error(gc)("Failed to commit memory (%d)", GetLastError()); + } + + unmap_view_no_placeholder(addr, size); + + return res == addr; +} + +uintptr_t ZMapper::map_view_no_placeholder(HANDLE file_handle, uintptr_t file_offset, size_t size) { + void* const res = ZSyscall::MapViewOfFile3( + file_handle, // FileMapping + GetCurrentProcess(), // ProcessHandle + NULL, // BaseAddress + file_offset, // Offset + size, // ViewSize + 0, // AllocationType + PAGE_NOACCESS, // PageProtection + NULL, // ExtendedParameters + 0 // ParameterCount + ); + + // Caller responsible for error handling + return (uintptr_t)res; +} + +void ZMapper::unmap_view_no_placeholder(uintptr_t addr, size_t size) { + const bool res = ZSyscall::UnmapViewOfFile2( + GetCurrentProcess(), // ProcessHandle + (void*)addr, // BaseAddress + 0 // UnmapFlags + ); + + if (!res) { + fatal_error("Failed to unmap memory", addr, size); + } +} + +uintptr_t ZMapper::commit(uintptr_t addr, size_t size) { + void* const res = ZSyscall::VirtualAlloc2( + GetCurrentProcess(), // Process + (void*)addr, // BaseAddress + size, // Size + MEM_COMMIT, // AllocationType + PAGE_NOACCESS, // PageProtection + NULL, // ExtendedParameters + 0 // ParameterCount + ); + + // Caller responsible for error handling + return (uintptr_t)res; +} + +HANDLE ZMapper::create_and_commit_paging_file_mapping(size_t size) { + HANDLE const file_handle = create_paging_file_mapping(size); + if (file_handle == 0) { + log_error(gc)("Failed to create paging file mapping (%d)", GetLastError()); + return 0; + } + + const bool res = commit_paging_file_mapping(file_handle, 0 /* file_offset */, size); + if (!res) { + close_paging_file_mapping(file_handle); + return 0; + } + + return file_handle; +} + +void ZMapper::close_paging_file_mapping(HANDLE file_handle) { + const bool res = CloseHandle( + file_handle // hObject + ); + + if (!res) { + fatal("Failed to close paging file handle (%d)", GetLastError()); + } +} + +void ZMapper::split_placeholder(uintptr_t addr, size_t size) { + const bool res = VirtualFree( + (void*)addr, // lpAddress + size, // dwSize + MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER // dwFreeType + ); + + if (!res) { + fatal_error("Failed to split placeholder", addr, size); + } +} + +void ZMapper::coalesce_placeholders(uintptr_t addr, size_t size) { + const bool res = VirtualFree( + (void*)addr, // lpAddress + size, // dwSize + MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS // dwFreeType + ); + + if (!res) { + fatal_error("Failed to coalesce placeholders", addr, size); + } +} + +void ZMapper::map_view_replace_placeholder(HANDLE file_handle, uintptr_t file_offset, uintptr_t addr, size_t size) { + void* const res = ZSyscall::MapViewOfFile3( + file_handle, // FileMapping + GetCurrentProcess(), // ProcessHandle + (void*)addr, // BaseAddress + file_offset, // Offset + size, // ViewSize + MEM_REPLACE_PLACEHOLDER, // AllocationType + PAGE_READWRITE, // PageProtection + NULL, // ExtendedParameters + 0 // ParameterCount + ); + + if (res == NULL) { + fatal_error("Failed to map memory", addr, size); + } +} + +void ZMapper::unmap_view_preserve_placeholder(uintptr_t addr, size_t size) { + const bool res = ZSyscall::UnmapViewOfFile2( + GetCurrentProcess(), // ProcessHandle + (void*)addr, // BaseAddress + MEM_PRESERVE_PLACEHOLDER // UnmapFlags + ); + + if (!res) { + fatal_error("Failed to unmap memory", addr, size); + } +} --- /dev/null 2019-10-01 11:37:31.106015611 +0200 +++ new/src/hotspot/os/windows/gc/z/zMapper_windows.hpp 2019-10-31 09:39:44.641040374 +0100 @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2019, 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. + * + * 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. + */ + +#ifndef OS_WINDOWS_GC_Z_ZMAPPER_WINDOWS_HPP +#define OS_WINDOWS_GC_Z_ZMAPPER_WINDOWS_HPP + +#include "memory/allocation.hpp" +#include "utilities/globalDefinitions.hpp" + +#include + +class ZMapper : public AllStatic { +private: + // Create paging file mapping + static HANDLE create_paging_file_mapping(size_t size); + + // Commit paging file mapping + static bool commit_paging_file_mapping(HANDLE file_handle, uintptr_t file_offset, size_t size); + + // Map a view anywhere without a placeholder + static uintptr_t map_view_no_placeholder(HANDLE file_handle, uintptr_t file_offset, size_t size); + + // Unmap a view without preserving a placeholder + static void unmap_view_no_placeholder(uintptr_t addr, size_t size); + + // Commit memory covering the given virtual address range + static uintptr_t commit(uintptr_t addr, size_t size); + +public: + // Reserve memory with a placeholder + static uintptr_t reserve(uintptr_t addr, size_t size); + + // Unreserve memory + static void unreserve(uintptr_t addr, size_t size); + + // Create and commit paging file mapping + static HANDLE create_and_commit_paging_file_mapping(size_t size); + + // Close paging file mapping + static void close_paging_file_mapping(HANDLE file_handle); + + // Split a placeholder + // + // A view can only replace an entire placeholder, so placeholders need to be + // split and coalesced to be the exact size of the new views. + // [addr, addr + size) needs to be a proper sub-placeholder of an existing + // placeholder. + static void split_placeholder(uintptr_t addr, size_t size); + + // Coalesce a placeholder + // + // [addr, addr + size) is the new placeholder. A sub-placeholder needs to + // exist within that range. + static void coalesce_placeholders(uintptr_t addr, size_t size); + + // Map a view of the file handle and replace the placeholder covering the + // given virtual address range + static void map_view_replace_placeholder(HANDLE file_handle, uintptr_t file_offset, uintptr_t addr, size_t size); + + // Unmap the view and reinstate a placeholder covering the given virtual + // address range + static void unmap_view_preserve_placeholder(uintptr_t addr, size_t size); +}; + +#endif // OS_WINDOWS_GC_Z_ZMAPPER_WINDOWS_HPP --- /dev/null 2019-10-01 11:37:31.106015611 +0200 +++ new/src/hotspot/os/windows/gc/z/zNUMA_windows.cpp 2019-10-31 09:39:45.241049972 +0100 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2019, 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. + * + * 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. + */ + +#include "precompiled.hpp" +#include "gc/z/zNUMA.hpp" + +void ZNUMA::initialize_platform() { + _enabled = false; +} + +uint32_t ZNUMA::count() { + return 1; +} + +uint32_t ZNUMA::id() { + return 0; +} + +uint32_t ZNUMA::memory_id(uintptr_t addr) { + // NUMA support not enabled, assume everything belongs to node zero + return 0; +} --- /dev/null 2019-10-01 11:37:31.106015611 +0200 +++ new/src/hotspot/os/windows/gc/z/zPhysicalMemoryBacking_windows.cpp 2019-10-31 09:39:45.897060465 +0100 @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2019, 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. + * + * 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. + */ + +#include "precompiled.hpp" +#include "gc/z/zAddress.inline.hpp" +#include "gc/z/zGlobals.hpp" +#include "gc/z/zLargePages.inline.hpp" +#include "gc/z/zMapper_windows.hpp" +#include "gc/z/zPhysicalMemory.inline.hpp" +#include "gc/z/zPhysicalMemoryBacking_windows.hpp" +#include "runtime/globals.hpp" +#include "runtime/init.hpp" +#include "runtime/os.hpp" +#include "utilities/align.hpp" +#include "utilities/debug.hpp" + +bool ZPhysicalMemoryBacking::is_initialized() const { + return true; +} + +void ZPhysicalMemoryBacking::warn_commit_limits(size_t max) const { + // Does nothing +} + +bool ZPhysicalMemoryBacking::supports_uncommit() { + assert(!is_init_completed(), "Invalid state"); + assert(_file.size() >= ZGranuleSize, "Invalid size"); + + // Test if uncommit is supported by uncommitting and then re-committing a granule + return commit(uncommit(ZGranuleSize)) == ZGranuleSize; +} + +size_t ZPhysicalMemoryBacking::commit(size_t size) { + size_t committed = 0; + + // Fill holes in the backing file + while (committed < size) { + size_t allocated = 0; + const size_t remaining = size - committed; + const uintptr_t start = _uncommitted.alloc_from_front_at_most(remaining, &allocated); + if (start == UINTPTR_MAX) { + // No holes to commit + break; + } + + // Try commit hole + const size_t filled = _file.commit(start, allocated); + if (filled > 0) { + // Successful or partialy successful + _committed.free(start, filled); + committed += filled; + } + if (filled < allocated) { + // Failed or partialy failed + _uncommitted.free(start + filled, allocated - filled); + return committed; + } + } + + // Expand backing file + if (committed < size) { + const size_t remaining = size - committed; + const uintptr_t start = _file.size(); + const size_t expanded = _file.commit(start, remaining); + if (expanded > 0) { + // Successful or partialy successful + _committed.free(start, expanded); + committed += expanded; + } + } + + return committed; +} + +size_t ZPhysicalMemoryBacking::uncommit(size_t size) { + size_t uncommitted = 0; + + // Punch holes in backing file + while (uncommitted < size) { + size_t allocated = 0; + const size_t remaining = size - uncommitted; + const uintptr_t start = _committed.alloc_from_back_at_most(remaining, &allocated); + assert(start != UINTPTR_MAX, "Allocation should never fail"); + + // Try punch hole + const size_t punched = _file.uncommit(start, allocated); + if (punched > 0) { + // Successful or partialy successful + _uncommitted.free(start, punched); + uncommitted += punched; + } + if (punched < allocated) { + // Failed or partialy failed + _committed.free(start + punched, allocated - punched); + return uncommitted; + } + } + + return uncommitted; +} + +ZPhysicalMemory ZPhysicalMemoryBacking::alloc(size_t size) { + assert(is_aligned(size, ZGranuleSize), "Invalid size"); + + ZPhysicalMemory pmem; + + // Allocate segments + for (size_t allocated = 0; allocated < size; allocated += ZGranuleSize) { + const uintptr_t start = _committed.alloc_from_front(ZGranuleSize); + assert(start != UINTPTR_MAX, "Allocation should never fail"); + pmem.add_segment(ZPhysicalMemorySegment(start, ZGranuleSize)); + } + + return pmem; +} + +void ZPhysicalMemoryBacking::free(const ZPhysicalMemory& pmem) { + const size_t nsegments = pmem.nsegments(); + + // Free segments + for (size_t i = 0; i < nsegments; i++) { + const ZPhysicalMemorySegment& segment = pmem.segment(i); + _committed.free(segment.start(), segment.size()); + } +} + +void ZPhysicalMemoryBacking::pretouch_view(uintptr_t addr, size_t size) const { + const size_t page_size = ZLargePages::is_explicit() ? os::large_page_size() : os::vm_page_size(); + os::pretouch_memory((void*)addr, (void*)(addr + size), page_size); +} + +void ZPhysicalMemoryBacking::map_view(const ZPhysicalMemory& pmem, uintptr_t addr, bool pretouch) const { + const size_t nsegments = pmem.nsegments(); + size_t size = 0; + + // Map segments + for (size_t i = 0; i < nsegments; i++) { + const ZPhysicalMemorySegment& segment = pmem.segment(i); + _file.map(addr + size, segment.size(), segment.start()); + size += segment.size(); + } + + // Pre-touch memory + if (pretouch) { + pretouch_view(addr, size); + } +} + +void ZPhysicalMemoryBacking::unmap_view(const ZPhysicalMemory& pmem, uintptr_t addr) const { + _file.unmap(addr, pmem.size()); +} + +uintptr_t ZPhysicalMemoryBacking::nmt_address(uintptr_t offset) const { + // From an NMT point of view we treat the first heap view (marked0) as committed + return ZAddress::marked0(offset); +} + +void ZPhysicalMemoryBacking::map(const ZPhysicalMemory& pmem, uintptr_t offset) const { + if (ZVerifyViews) { + // Map good view + map_view(pmem, ZAddress::good(offset), AlwaysPreTouch); + } else { + // Map all views + map_view(pmem, ZAddress::marked0(offset), AlwaysPreTouch); + map_view(pmem, ZAddress::marked1(offset), AlwaysPreTouch); + map_view(pmem, ZAddress::remapped(offset), AlwaysPreTouch); + } +} + +void ZPhysicalMemoryBacking::unmap(const ZPhysicalMemory& pmem, uintptr_t offset) const { + if (ZVerifyViews) { + // Unmap good view + unmap_view(pmem, ZAddress::good(offset)); + } else { + // Unmap all views + unmap_view(pmem, ZAddress::marked0(offset)); + unmap_view(pmem, ZAddress::marked1(offset)); + unmap_view(pmem, ZAddress::remapped(offset)); + } +} + +void ZPhysicalMemoryBacking::debug_map(const ZPhysicalMemory& pmem, uintptr_t offset) const { + // Map good view + assert(ZVerifyViews, "Should be enabled"); + map_view(pmem, ZAddress::good(offset), false /* pretouch */); +} + +void ZPhysicalMemoryBacking::debug_unmap(const ZPhysicalMemory& pmem, uintptr_t offset) const { + // Unmap good view + assert(ZVerifyViews, "Should be enabled"); + unmap_view(pmem, ZAddress::good(offset)); +} --- /dev/null 2019-10-01 11:37:31.106015611 +0200 +++ new/src/hotspot/os/windows/gc/z/zPhysicalMemoryBacking_windows.hpp 2019-10-31 09:39:46.529070575 +0100 @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2019, 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. + * + * 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. + */ + +#ifndef OS_WINDOWS_GC_Z_ZPHYSICALMEMORYBACKING_WINDOWS_HPP +#define OS_WINDOWS_GC_Z_ZPHYSICALMEMORYBACKING_WINDOWS_HPP + +#include "gc/z/zBackingFile_windows.hpp" +#include "gc/z/zMemory.hpp" + +class ZPhysicalMemory; + +class ZPhysicalMemoryBacking { +private: + ZBackingFile _file; + ZMemoryManager _committed; + ZMemoryManager _uncommitted; + + void pretouch_view(uintptr_t addr, size_t size) const; + void map_view(const ZPhysicalMemory& pmem, uintptr_t addr, bool pretouch) const; + void unmap_view(const ZPhysicalMemory& pmem, uintptr_t addr) const; + +public: + bool is_initialized() const; + + void warn_commit_limits(size_t max) const; + bool supports_uncommit(); + + size_t commit(size_t size); + size_t uncommit(size_t size); + + ZPhysicalMemory alloc(size_t size); + void free(const ZPhysicalMemory& pmem); + + uintptr_t nmt_address(uintptr_t offset) const; + + void map(const ZPhysicalMemory& pmem, uintptr_t offset) const; + void unmap(const ZPhysicalMemory& pmem, uintptr_t offset) const; + + void debug_map(const ZPhysicalMemory& pmem, uintptr_t offset) const; + void debug_unmap(const ZPhysicalMemory& pmem, uintptr_t offset) const; +}; + +#endif // OS_WINDOWS_GC_Z_ZPHYSICALMEMORYBACKING_WINDOWS_HPP --- /dev/null 2019-10-01 11:37:31.106015611 +0200 +++ new/src/hotspot/os/windows/gc/z/zSyscall_windows.cpp 2019-10-31 09:39:47.165080748 +0100 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019, 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. + * + * 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. + */ + +#include "precompiled.hpp" +#include "gc/z/zSyscall_windows.hpp" +#include "logging/log.hpp" +#include "runtime/java.hpp" +#include "runtime/os.hpp" + +ZSyscall::CreateFileMappingWFn ZSyscall::CreateFileMappingW; +ZSyscall::VirtualAlloc2Fn ZSyscall::VirtualAlloc2; +ZSyscall::VirtualFreeExFn ZSyscall::VirtualFreeEx; +ZSyscall::MapViewOfFile3Fn ZSyscall::MapViewOfFile3; +ZSyscall::UnmapViewOfFile2Fn ZSyscall::UnmapViewOfFile2; + +template +static void lookup_symbol(Fn*& fn, const char* library, const char* symbol) { + char ebuf[1024]; + void* const handle = os::dll_load(library, ebuf, sizeof(ebuf)); + if (handle == NULL) { + log_error(gc)("Failed to load library: %s", library); + vm_exit_during_initialization("ZGC requires Windows version 1803 or later"); + } + + fn = reinterpret_cast(os::dll_lookup(handle, symbol)); + if (fn == NULL) { + log_error(gc)("Failed to lookup symbol: %s", symbol); + vm_exit_during_initialization("ZGC requires Windows version 1803 or later"); + } +} + +void ZSyscall::initialize() { + lookup_symbol(CreateFileMappingW, "KernelBase", "CreateFileMappingW"); + lookup_symbol(VirtualAlloc2, "KernelBase", "VirtualAlloc2"); + lookup_symbol(VirtualFreeEx, "KernelBase", "VirtualFreeEx"); + lookup_symbol(MapViewOfFile3, "KernelBase", "MapViewOfFile3"); + lookup_symbol(UnmapViewOfFile2, "KernelBase", "UnmapViewOfFile2"); +} --- /dev/null 2019-10-01 11:37:31.106015611 +0200 +++ new/src/hotspot/os/windows/gc/z/zSyscall_windows.hpp 2019-10-31 09:39:47.817091177 +0100 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2019, 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. + * + * 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. + */ + +#ifndef OS_WINDOWS_GC_Z_ZSYSCALL_WINDOWS_HPP +#define OS_WINDOWS_GC_Z_ZSYSCALL_WINDOWS_HPP + +#include "utilities/globalDefinitions.hpp" + +#include +#include + +class ZSyscall { +private: + typedef HANDLE (*CreateFileMappingWFn)(HANDLE, LPSECURITY_ATTRIBUTES, DWORD, DWORD, DWORD, LPCWSTR); + typedef PVOID (*VirtualAlloc2Fn)(HANDLE, PVOID, SIZE_T, ULONG, ULONG, MEM_EXTENDED_PARAMETER*, ULONG); + typedef BOOL (*VirtualFreeExFn)(HANDLE, LPVOID, SIZE_T, DWORD); + typedef PVOID (*MapViewOfFile3Fn)(HANDLE, HANDLE, PVOID, ULONG64, SIZE_T, ULONG, ULONG, MEM_EXTENDED_PARAMETER*, ULONG); + typedef BOOL (*UnmapViewOfFile2Fn)(HANDLE, PVOID, ULONG); + +public: + static CreateFileMappingWFn CreateFileMappingW; + static VirtualAlloc2Fn VirtualAlloc2; + static VirtualFreeExFn VirtualFreeEx; + static MapViewOfFile3Fn MapViewOfFile3; + static UnmapViewOfFile2Fn UnmapViewOfFile2; + + static void initialize(); +}; + +#endif // OS_WINDOWS_GC_Z_ZSYSCALL_WINDOWS_HPP --- /dev/null 2019-10-01 11:37:31.106015611 +0200 +++ new/src/hotspot/os/windows/gc/z/zUtils_windows.cpp 2019-10-31 09:39:48.457101415 +0100 @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019, 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. + * + * 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. + */ + +#include "precompiled.hpp" +#include "gc/z/zUtils.hpp" +#include "utilities/debug.hpp" + +#include + +uintptr_t ZUtils::alloc_aligned(size_t alignment, size_t size) { + void* const res = _aligned_malloc(size, alignment); + + if (res == NULL) { + fatal("_aligned_malloc failed"); + } + + memset(res, 0, size); + + return (uintptr_t)res; +} --- /dev/null 2019-10-01 11:37:31.106015611 +0200 +++ new/src/hotspot/os/windows/gc/z/zVirtualMemory_windows.cpp 2019-10-31 09:39:49.105111780 +0100 @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2019, 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. + * + * 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. + */ + +#include "precompiled.hpp" +#include "gc/z/zAddress.inline.hpp" +#include "gc/z/zGlobals.hpp" +#include "gc/z/zMapper_windows.hpp" +#include "gc/z/zVirtualMemory.hpp" +#include "utilities/align.hpp" +#include "utilities/debug.hpp" + +static void split_placeholder(uintptr_t start, size_t size) { + ZMapper::split_placeholder(ZAddress::marked0(start), size); + ZMapper::split_placeholder(ZAddress::marked1(start), size); + ZMapper::split_placeholder(ZAddress::remapped(start), size); +} + +static void coalesce_placeholders(uintptr_t start, size_t size) { + ZMapper::coalesce_placeholders(ZAddress::marked0(start), size); + ZMapper::coalesce_placeholders(ZAddress::marked1(start), size); + ZMapper::coalesce_placeholders(ZAddress::remapped(start), size); +} + +static void split_into_placeholder_granules(uintptr_t start, size_t size) { + for (uintptr_t addr = start; addr < start + size; addr += ZGranuleSize) { + split_placeholder(addr, ZGranuleSize); + } +} + +static void coalesce_into_one_placeholder(uintptr_t start, size_t size) { + assert(is_aligned(size, ZGranuleSize), "Must be granule aligned"); + + if (size > ZGranuleSize) { + coalesce_placeholders(start, size); + } +} + +static void create_callback(const ZMemory* area) { + assert(is_aligned(area->size(), ZGranuleSize), "Must be granule aligned"); + coalesce_into_one_placeholder(area->start(), area->size()); +} + +static void destroy_callback(const ZMemory* area) { + assert(is_aligned(area->size(), ZGranuleSize), "Must be granule aligned"); + // Don't try split the last granule - VirtualFree will fail + split_into_placeholder_granules(area->start(), area->size() - ZGranuleSize); +} + +static void shrink_from_front_callback(const ZMemory* area, size_t size) { + assert(is_aligned(size, ZGranuleSize), "Must be granule aligned"); + split_into_placeholder_granules(area->start(), size); +} + +static void shrink_from_back_callback(const ZMemory* area, size_t size) { + assert(is_aligned(size, ZGranuleSize), "Must be granule aligned"); + // Don't try split the last granule - VirtualFree will fail + split_into_placeholder_granules(area->end() - size, size - ZGranuleSize); +} + +static void grow_from_front_callback(const ZMemory* area, size_t size) { + assert(is_aligned(area->size(), ZGranuleSize), "Must be granule aligned"); + coalesce_into_one_placeholder(area->start() - size, area->size() + size); +} + +static void grow_from_back_callback(const ZMemory* area, size_t size) { + assert(is_aligned(area->size(), ZGranuleSize), "Must be granule aligned"); + coalesce_into_one_placeholder(area->start(), area->size() + size); +} + +void ZVirtualMemoryManager::initialize_os() { + // Each reserved virtual memory address area registered in _manager is + // exactly covered by a single placeholder. Callbacks are installed so + // that whenever a memory area changes, the corresponding placeholder + // is adjusted. + // + // The create and grow callbacks are called when virtual memory is + // returned to the memory manager. The new memory area is then covered + // by a new single placeholder. + // + // The destroy and shrink callbacks are called when virtual memory is + // allocated from the memory manager. The memory area is then is split + // into granule-sized placeholders. + // + // See comment in zMapper_windows.cpp explaining why placeholders are + // split into ZGranuleSize sized placeholders. + + ZMemoryManager::Callbacks callbacks; + + callbacks._create = &create_callback; + callbacks._destroy = &destroy_callback; + callbacks._shrink_from_front = &shrink_from_front_callback; + callbacks._shrink_from_back = &shrink_from_back_callback; + callbacks._grow_from_front = &grow_from_front_callback; + callbacks._grow_from_back = &grow_from_back_callback; + + _manager.register_callbacks(callbacks); +} + +bool ZVirtualMemoryManager::reserve_contiguous_platform(uintptr_t start, size_t size) { + assert(is_aligned(size, ZGranuleSize), "Must be granule aligned"); + + // Reserve address views + const uintptr_t marked0 = ZAddress::marked0(start); + const uintptr_t marked1 = ZAddress::marked1(start); + const uintptr_t remapped = ZAddress::remapped(start); + + // Reserve address space + if (ZMapper::reserve(marked0, size) != marked0) { + return false; + } + + if (ZMapper::reserve(marked1, size) != marked1) { + ZMapper::unreserve(marked0, size); + return false; + } + + if (ZMapper::reserve(remapped, size) != remapped) { + ZMapper::unreserve(marked0, size); + ZMapper::unreserve(marked1, size); + return false; + } + + // Register address views with native memory tracker + nmt_reserve(marked0, size); + nmt_reserve(marked1, size); + nmt_reserve(remapped, size); + + return true; +}