changeset 4717:f2a9de120e2d

8014611: reserve_and_align() assumptions are invalid on windows Summary: also reviewed by ron.durbin@oracle.com, thomas.schatzl@oracle.com Reviewed-by: dcubed, brutisso
author jcoomes
date Thu, 23 May 2013 03:08:19 -0700
parents 044681b8bab0
children e50c5a1869b1
files src/os/posix/vm/os_posix.cpp src/os/windows/vm/os_windows.cpp src/share/vm/runtime/os.cpp src/share/vm/runtime/os.hpp src/share/vm/runtime/virtualspace.cpp src/share/vm/runtime/virtualspace.hpp
diffstat 6 files changed, 75 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
--- a/src/os/posix/vm/os_posix.cpp	Thu May 23 08:14:09 2013 +0200
+++ b/src/os/posix/vm/os_posix.cpp	Thu May 23 03:08:19 2013 -0700
@@ -134,6 +134,10 @@
   return aligned_base;
 }
 
+bool os::can_release_partial_region() {
+  return true;
+}
+
 void os::Posix::print_load_average(outputStream* st) {
   st->print("load average:");
   double loadavg[3];
--- a/src/os/windows/vm/os_windows.cpp	Thu May 23 08:14:09 2013 +0200
+++ b/src/os/windows/vm/os_windows.cpp	Thu May 23 03:08:19 2013 -0700
@@ -2971,6 +2971,10 @@
   }
 }
 
+bool os::can_release_partial_region() {
+  return false;
+}
+
 // Multiple threads can race in this code but it's not possible to unmap small sections of
 // virtual space to get requested alignment, like posix-like os's.
 // Windows prevents multiple thread from remapping over each other so this loop is thread-safe.
--- a/src/share/vm/runtime/os.cpp	Thu May 23 08:14:09 2013 +0200
+++ b/src/share/vm/runtime/os.cpp	Thu May 23 03:08:19 2013 -0700
@@ -1461,6 +1461,12 @@
   return res;
 }
 
+bool os::release_or_uncommit_partial_region(char * addr, size_t bytes) {
+  if (can_release_partial_region()) {
+    return release_memory(addr, bytes);
+  }
+  return uncommit_memory(addr, bytes);
+}
 
 char* os::map_memory(int fd, const char* file_name, size_t file_offset,
                            char *addr, size_t bytes, bool read_only,
--- a/src/share/vm/runtime/os.hpp	Thu May 23 08:14:09 2013 +0200
+++ b/src/share/vm/runtime/os.hpp	Thu May 23 03:08:19 2013 -0700
@@ -266,6 +266,8 @@
                               bool executable = false);
   static bool   uncommit_memory(char* addr, size_t bytes);
   static bool   release_memory(char* addr, size_t bytes);
+  static bool   can_release_partial_region();
+  static bool   release_or_uncommit_partial_region(char* addr, size_t bytes);
 
   enum ProtType { MEM_PROT_NONE, MEM_PROT_READ, MEM_PROT_RW, MEM_PROT_RWX };
   static bool   protect_memory(char* addr, size_t bytes, ProtType prot,
--- a/src/share/vm/runtime/virtualspace.cpp	Thu May 23 08:14:09 2013 +0200
+++ b/src/share/vm/runtime/virtualspace.cpp	Thu May 23 03:08:19 2013 -0700
@@ -81,17 +81,41 @@
   const size_t end_delta = len - (beg_delta + required_size);
 
   if (beg_delta != 0) {
-    os::release_memory(addr, beg_delta);
+    os::release_or_uncommit_partial_region(addr, beg_delta);
   }
 
   if (end_delta != 0) {
     char* release_addr = (char*) (s + beg_delta + required_size);
-    os::release_memory(release_addr, end_delta);
+    os::release_or_uncommit_partial_region(release_addr, end_delta);
   }
 
   return (char*) (s + beg_delta);
 }
 
+void ReservedSpace::set_raw_base_and_size(char * const raw_base,
+                                          size_t raw_size) {
+  assert(raw_base == NULL || !os::can_release_partial_region(), "sanity");
+  _raw_base = raw_base;
+  _raw_size = raw_size;
+}
+
+// On some systems (e.g., windows), the address returned by os::reserve_memory()
+// is the only addr that can be passed to os::release_memory().  If alignment
+// was done by this class, that original address is _raw_base.
+void ReservedSpace::release_memory(char* default_addr, size_t default_size) {
+  bool ok;
+  if (_raw_base == NULL) {
+    ok = os::release_memory(default_addr, default_size);
+  } else {
+    assert(!os::can_release_partial_region(), "sanity");
+    ok = os::release_memory(_raw_base, _raw_size);
+  }
+  if (!ok) {
+    fatal("os::release_memory failed");
+  }
+  set_raw_base_and_size(NULL, 0);
+}
+
 char* ReservedSpace::reserve_and_align(const size_t reserve_size,
                                        const size_t prefix_size,
                                        const size_t prefix_align,
@@ -110,6 +134,10 @@
     fatal("os::release_memory failed");
   }
 
+  if (!os::can_release_partial_region()) {
+    set_raw_base_and_size(raw_addr, reserve_size);
+  }
+
 #ifdef ASSERT
   if (result != NULL) {
     const size_t raw = size_t(raw_addr);
@@ -127,8 +155,10 @@
 }
 
 // Helper method.
-static bool failed_to_reserve_as_requested(char* base, char* requested_address,
-                                           const size_t size, bool special)
+bool ReservedSpace::failed_to_reserve_as_requested(char* base,
+                                                   char* requested_address,
+                                                   const size_t size,
+                                                   bool special)
 {
   if (base == requested_address || requested_address == NULL)
     return false; // did not fail
@@ -147,9 +177,7 @@
         fatal("os::release_memory_special failed");
       }
     } else {
-      if (!os::release_memory(base, size)) {
-        fatal("os::release_memory failed");
-      }
+      release_memory(base, size);
     }
   }
   return true;
@@ -177,6 +205,8 @@
   assert(noaccess_prefix == 0 ||
          noaccess_prefix == prefix_align, "noaccess prefix wrong");
 
+  set_raw_base_and_size(NULL, 0);
+
   // Add in noaccess_prefix to prefix_size;
   const size_t adjusted_prefix_size = prefix_size + noaccess_prefix;
   const size_t size = adjusted_prefix_size + suffix_size;
@@ -224,9 +254,7 @@
     // result is often the same address (if the kernel hands out virtual
     // addresses from low to high), or an address that is offset by the increase
     // in size.  Exploit that to minimize the amount of extra space requested.
-    if (!os::release_memory(addr, size)) {
-      fatal("os::release_memory failed");
-    }
+    release_memory(addr, size);
 
     const size_t extra = MAX2(ofs, suffix_align - ofs);
     addr = reserve_and_align(size + extra, adjusted_prefix_size, prefix_align,
@@ -265,6 +293,8 @@
   assert(alignment == 0 || is_power_of_2((intptr_t)alignment),
          "not a power of 2");
 
+  set_raw_base_and_size(NULL, 0);
+
   alignment = MAX2(alignment, (size_t)os::vm_page_size());
 
   // Assert that if noaccess_prefix is used, it is the same as alignment.
@@ -340,7 +370,8 @@
     // Check alignment constraints
     if ((((size_t)base + noaccess_prefix) & (alignment - 1)) != 0) {
       // Base not aligned, retry
-      if (!os::release_memory(base, size)) fatal("os::release_memory failed");
+      release_memory(base, size);
+
       // Make sure that size is aligned
       size = align_size_up(size, alignment);
       base = os::reserve_memory_aligned(size, alignment);
@@ -378,6 +409,7 @@
          "size not allocation aligned");
   _base = base;
   _size = size;
+  set_raw_base_and_size(NULL, 0);
   _alignment = alignment;
   _noaccess_prefix = 0;
   _special = special;
@@ -433,7 +465,7 @@
     if (special()) {
       os::release_memory_special(real_base, real_size);
     } else{
-      os::release_memory(real_base, real_size);
+      release_memory(real_base, real_size);
     }
     _base = NULL;
     _size = 0;
--- a/src/share/vm/runtime/virtualspace.hpp	Thu May 23 08:14:09 2013 +0200
+++ b/src/share/vm/runtime/virtualspace.hpp	Thu May 23 03:08:19 2013 -0700
@@ -35,6 +35,12 @@
   char*  _base;
   size_t _size;
   size_t _noaccess_prefix;
+
+  // The base and size prior to any alignment done by this class; used only on
+  // systems that cannot release part of a region.
+  char*  _raw_base;
+  size_t _raw_size;
+
   size_t _alignment;
   bool   _special;
   bool   _executable;
@@ -42,11 +48,20 @@
   // ReservedSpace
   ReservedSpace(char* base, size_t size, size_t alignment, bool special,
                 bool executable);
+
+  bool failed_to_reserve_as_requested(char* base, char* requested_address,
+                                      const size_t size, bool special);
   void initialize(size_t size, size_t alignment, bool large,
                   char* requested_address,
                   const size_t noaccess_prefix,
                   bool executable);
 
+  inline void set_raw_base_and_size(char * const raw_base, size_t raw_size);
+
+  // Release virtual address space.  If alignment was done, use the saved
+  // address and size when releasing.
+  void release_memory(char * default_addr, size_t default_size);
+
   // Release parts of an already-reserved memory region [addr, addr + len) to
   // get a new region that has "compound alignment."  Return the start of the
   // resulting region, or NULL on failure.