]> code.delx.au - gnu-emacs/blobdiff - src/w32heap.c
Minor tweaks of copying text properties when padding strings
[gnu-emacs] / src / w32heap.c
index ee0eb161502e448f80e1931d7d9b0375f7f406f3..658a8a5d691b08aebefaadd9ead4f2394179e0d7 100644 (file)
@@ -1,5 +1,5 @@
 /* Heap management routines for GNU Emacs on the Microsoft Windows API.
 /* Heap management routines for GNU Emacs on the Microsoft Windows API.
-   Copyright (C) 1994, 2001-2015 Free Software Foundation, Inc.
+   Copyright (C) 1994, 2001-2016 Free Software Foundation, Inc.
 
    This file is part of GNU Emacs.
 
 
    This file is part of GNU Emacs.
 
@@ -52,7 +52,7 @@
 #include <sys/mman.h>
 #include "w32common.h"
 #include "w32heap.h"
 #include <sys/mman.h>
 #include "w32common.h"
 #include "w32heap.h"
-#include "lisp.h"  /* for VALMASK */
+#include "lisp.h"
 
 /* We chose to leave those declarations here.  They are used only in
    this file.  The RtlCreateHeap is available since XP.  It is located
 
 /* We chose to leave those declarations here.  They are used only in
    this file.  The RtlCreateHeap is available since XP.  It is located
@@ -73,12 +73,11 @@ typedef PVOID (WINAPI * RtlCreateHeap_Proc) (
 
 typedef LONG NTSTATUS;
 
 
 typedef LONG NTSTATUS;
 
-typedef NTSTATUS
-(NTAPI * PRTL_HEAP_COMMIT_ROUTINE)(
-                                   IN PVOID Base,
-                                   IN OUT PVOID *CommitAddress,
-                                   IN OUT PSIZE_T CommitSize
-                                   );
+typedef NTSTATUS (NTAPI *PRTL_HEAP_COMMIT_ROUTINE) (
+                                                   IN PVOID Base,
+                                                   IN OUT PVOID *CommitAddress,
+                                                   IN OUT PSIZE_T CommitSize
+                                                   );
 
 typedef struct _RTL_HEAP_PARAMETERS {
   ULONG Length;
 
 typedef struct _RTL_HEAP_PARAMETERS {
   ULONG Length;
@@ -100,8 +99,8 @@ typedef struct _RTL_HEAP_PARAMETERS {
    special segment to the executable.  In order to be able to do this
    without losing too much space, we need to create a Windows heap at
    the specific address of the static array.  The RtlCreateHeap
    special segment to the executable.  In order to be able to do this
    without losing too much space, we need to create a Windows heap at
    the specific address of the static array.  The RtlCreateHeap
-   available inside the NT kernel since XP will do this.  It allows to
-   create a non-growable heap at a specific address.  So before
+   available inside the NT kernel since XP will do this.  It allows the
+   creation of a non-growable heap at a specific address.  So before
    dumping, we create a non-growable heap at the address of the
    dumped_data[] array.  After dumping, we reuse memory allocated
    there without being able to free it (but most of it is not meant to
    dumping, we create a non-growable heap at the address of the
    dumped_data[] array.  After dumping, we reuse memory allocated
    there without being able to free it (but most of it is not meant to
@@ -115,9 +114,9 @@ typedef struct _RTL_HEAP_PARAMETERS {
    to build only the first bootstrap-emacs.exe with the large size,
    and reset that to a lower value afterwards.  */
 #if defined _WIN64 || defined WIDE_EMACS_INT
    to build only the first bootstrap-emacs.exe with the large size,
    and reset that to a lower value afterwards.  */
 #if defined _WIN64 || defined WIDE_EMACS_INT
-# define DUMPED_HEAP_SIZE (18*1024*1024)
+# define DUMPED_HEAP_SIZE (20*1024*1024)
 #else
 #else
-# define DUMPED_HEAP_SIZE (11*1024*1024)
+# define DUMPED_HEAP_SIZE (12*1024*1024)
 #endif
 
 static unsigned char dumped_data[DUMPED_HEAP_SIZE];
 #endif
 
 static unsigned char dumped_data[DUMPED_HEAP_SIZE];
@@ -190,7 +189,7 @@ free_fn the_free_fn;
    claims for new memory.  Before dumping, we allocate space
    from the fixed size dumped_data[] array.
 */
    claims for new memory.  Before dumping, we allocate space
    from the fixed size dumped_data[] array.
 */
-NTSTATUS NTAPI
+static NTSTATUS NTAPI
 dumped_data_commit (PVOID Base, PVOID *CommitAddress, PSIZE_T CommitSize)
 {
   /* This is used before dumping.
 dumped_data_commit (PVOID Base, PVOID *CommitAddress, PSIZE_T CommitSize)
 {
   /* This is used before dumping.
@@ -258,9 +257,18 @@ init_heap (void)
        }
 #endif
 
        }
 #endif
 
-      the_malloc_fn = malloc_after_dump;
-      the_realloc_fn = realloc_after_dump;
-      the_free_fn = free_after_dump;
+      if (os_subtype == OS_9X)
+        {
+          the_malloc_fn = malloc_after_dump_9x;
+          the_realloc_fn = realloc_after_dump_9x;
+          the_free_fn = free_after_dump_9x;
+        }
+      else
+        {
+          the_malloc_fn = malloc_after_dump;
+          the_realloc_fn = realloc_after_dump;
+          the_free_fn = free_after_dump;
+        }
     }
   else
     {
     }
   else
     {
@@ -291,9 +299,18 @@ init_heap (void)
          exit (-1);
        }
       heap = s_pfn_Rtl_Create_Heap (0, data_region_base, 0, 0, NULL, &params);
          exit (-1);
        }
       heap = s_pfn_Rtl_Create_Heap (0, data_region_base, 0, 0, NULL, &params);
-      the_malloc_fn = malloc_before_dump;
-      the_realloc_fn = realloc_before_dump;
-      the_free_fn = free_before_dump;
+
+      if (os_subtype == OS_9X)
+        {
+          fprintf (stderr, "Cannot dump Emacs on Windows 9X; exiting.\n");
+          exit (-1);
+        }
+      else
+        {
+          the_malloc_fn = malloc_before_dump;
+          the_realloc_fn = realloc_before_dump;
+          the_free_fn = free_before_dump;
+        }
     }
 
   /* Update system version information to match current system.  */
     }
 
   /* Update system version information to match current system.  */
@@ -305,9 +322,10 @@ init_heap (void)
 #undef free
 
 /* FREEABLE_P checks if the block can be safely freed.  */
 #undef free
 
 /* FREEABLE_P checks if the block can be safely freed.  */
-#define FREEABLE_P(addr)                                        \
-    ((unsigned char *)(addr) < dumped_data                      \
-     || (unsigned char *)(addr) >= dumped_data + DUMPED_HEAP_SIZE)
+#define FREEABLE_P(addr)                                               \
+  ((DWORD_PTR)(unsigned char *)(addr) > 0                              \
+   && ((unsigned char *)(addr) < dumped_data                           \
+       || (unsigned char *)(addr) >= dumped_data + DUMPED_HEAP_SIZE))
 
 void *
 malloc_after_dump (size_t size)
 
 void *
 malloc_after_dump (size_t size)
@@ -407,10 +425,10 @@ realloc_after_dump (void *ptr, size_t size)
       /* If the block lies in the dumped data, do not free it.  Only
          allocate a new one.  */
       p = HeapAlloc (heap, 0, size);
       /* If the block lies in the dumped data, do not free it.  Only
          allocate a new one.  */
       p = HeapAlloc (heap, 0, size);
-      if (p)
-       CopyMemory (p, ptr, size);
-      else
+      if (!p)
        errno = ENOMEM;
        errno = ENOMEM;
+      else if (ptr)
+       CopyMemory (p, ptr, size);
     }
   /* After dump, keep track of the "brk value" for sbrk(0).  */
   if (p)
     }
   /* After dump, keep track of the "brk value" for sbrk(0).  */
   if (p)
@@ -449,7 +467,7 @@ realloc_before_dump (void *ptr, size_t size)
         of failing the call as below.  But this doesn't seem to be
         worth the added complexity, as loadup allocates only a very
         small number of large blocks, and never reallocates them.  */
         of failing the call as below.  But this doesn't seem to be
         worth the added complexity, as loadup allocates only a very
         small number of large blocks, and never reallocates them.  */
-      if (p)
+      if (p && ptr)
        {
          CopyMemory (p, ptr, size);
          free_before_dump (ptr);
        {
          CopyMemory (p, ptr, size);
          free_before_dump (ptr);
@@ -473,6 +491,9 @@ free_after_dump (void *ptr)
 void
 free_before_dump (void *ptr)
 {
 void
 free_before_dump (void *ptr)
 {
+  if (!ptr)
+    return;
+
   /* Before dumping.  */
   if (dumped_data < (unsigned char *)ptr
       && (unsigned char *)ptr < bc_limit)
   /* Before dumping.  */
   if (dumped_data < (unsigned char *)ptr
       && (unsigned char *)ptr < bc_limit)
@@ -500,6 +521,65 @@ free_before_dump (void *ptr)
     }
 }
 
     }
 }
 
+/* On Windows 9X, HeapAlloc may return pointers that are not aligned
+   on 8-byte boundary, alignment which is required by the Lisp memory
+   management.  To circumvent this problem, manually enforce alignment
+   on Windows 9X.  */
+
+void *
+malloc_after_dump_9x (size_t size)
+{
+  void *p = malloc_after_dump (size + 8);
+  void *pa;
+  if (p == NULL)
+    return p;
+  pa = (void*)(((intptr_t)p + 8) & ~7);
+  *((void**)pa-1) = p;
+  return pa;
+}
+
+void *
+realloc_after_dump_9x (void *ptr, size_t size)
+{
+  if (FREEABLE_P (ptr))
+    {
+      void *po = *((void**)ptr-1);
+      void *p;
+      void *pa;
+      p = realloc_after_dump (po, size + 8);
+      if (p == NULL)
+        return p;
+      pa = (void*)(((intptr_t)p + 8) & ~7);
+      if (ptr != NULL &&
+          (char*)pa - (char*)p != (char*)ptr - (char*)po)
+        {
+          /* Handle the case where alignment in pre-realloc and
+             post-realloc blocks does not match.  */
+          MoveMemory (pa, (void*)((char*)p + ((char*)ptr - (char*)po)), size);
+        }
+      *((void**)pa-1) = p;
+      return pa;
+    }
+  else
+    {
+      /* Non-freeable pointers have no alignment-enforcing header
+         (since dumping is not allowed on Windows 9X).  */
+      void* p = malloc_after_dump_9x (size);
+      if (p != NULL)
+       CopyMemory (p, ptr, size);
+      return p;
+    }
+}
+
+void
+free_after_dump_9x (void *ptr)
+{
+  if (FREEABLE_P (ptr))
+    {
+      free_after_dump (*((void**)ptr-1));
+    }
+}
+
 #ifdef ENABLE_CHECKING
 void
 report_temacs_memory_usage (void)
 #ifdef ENABLE_CHECKING
 void
 report_temacs_memory_usage (void)
@@ -560,26 +640,32 @@ mmap_alloc (void **var, size_t nbytes)
      advance, and the buffer is enlarged several times as the data is
      decompressed on the fly.  */
   if (nbytes < MAX_BUFFER_SIZE)
      advance, and the buffer is enlarged several times as the data is
      decompressed on the fly.  */
   if (nbytes < MAX_BUFFER_SIZE)
-    p = VirtualAlloc (NULL, (nbytes * 2), MEM_RESERVE, PAGE_READWRITE);
+    p = VirtualAlloc (NULL, ROUND_UP (nbytes * 2, get_allocation_unit ()),
+                     MEM_RESERVE, PAGE_READWRITE);
 
   /* If it fails, or if the request is above 512MB, try with the
      requested size.  */
   if (p == NULL)
 
   /* If it fails, or if the request is above 512MB, try with the
      requested size.  */
   if (p == NULL)
-    p = VirtualAlloc (NULL, nbytes, MEM_RESERVE, PAGE_READWRITE);
+    p = VirtualAlloc (NULL, ROUND_UP (nbytes, get_allocation_unit ()),
+                     MEM_RESERVE, PAGE_READWRITE);
 
   if (p != NULL)
     {
       /* Now, commit pages for NBYTES.  */
       *var = VirtualAlloc (p, nbytes, MEM_COMMIT, PAGE_READWRITE);
 
   if (p != NULL)
     {
       /* Now, commit pages for NBYTES.  */
       *var = VirtualAlloc (p, nbytes, MEM_COMMIT, PAGE_READWRITE);
+      if (*var == NULL)
+       p = *var;
     }
 
   if (!p)
     {
     }
 
   if (!p)
     {
-      if (GetLastError () == ERROR_NOT_ENOUGH_MEMORY)
+      DWORD e = GetLastError ();
+
+      if (e == ERROR_NOT_ENOUGH_MEMORY)
        errno = ENOMEM;
       else
        {
        errno = ENOMEM;
       else
        {
-         DebPrint (("mmap_alloc: error %ld\n", GetLastError ()));
+         DebPrint (("mmap_alloc: error %ld\n", e));
          errno = EINVAL;
        }
     }
          errno = EINVAL;
        }
     }
@@ -602,6 +688,7 @@ void *
 mmap_realloc (void **var, size_t nbytes)
 {
   MEMORY_BASIC_INFORMATION memInfo, m2;
 mmap_realloc (void **var, size_t nbytes)
 {
   MEMORY_BASIC_INFORMATION memInfo, m2;
+  void *old_ptr;
 
   if (*var == NULL)
     return mmap_alloc (var, nbytes);
 
   if (*var == NULL)
     return mmap_alloc (var, nbytes);
@@ -613,52 +700,54 @@ mmap_realloc (void **var, size_t nbytes)
       return mmap_alloc (var, nbytes);
     }
 
       return mmap_alloc (var, nbytes);
     }
 
+  memset (&memInfo, 0, sizeof (memInfo));
   if (VirtualQuery (*var, &memInfo, sizeof (memInfo)) == 0)
     DebPrint (("mmap_realloc: VirtualQuery error = %ld\n", GetLastError ()));
 
   /* We need to enlarge the block.  */
   if (memInfo.RegionSize < nbytes)
     {
   if (VirtualQuery (*var, &memInfo, sizeof (memInfo)) == 0)
     DebPrint (("mmap_realloc: VirtualQuery error = %ld\n", GetLastError ()));
 
   /* We need to enlarge the block.  */
   if (memInfo.RegionSize < nbytes)
     {
-      if (VirtualQuery (*var + memInfo.RegionSize, &m2, sizeof(m2)) == 0)
+      memset (&m2, 0, sizeof (m2));
+      if (VirtualQuery ((char *)*var + memInfo.RegionSize, &m2, sizeof(m2)) == 0)
         DebPrint (("mmap_realloc: VirtualQuery error = %ld\n",
                   GetLastError ()));
       /* If there is enough room in the current reserved area, then
         commit more pages as needed.  */
       if (m2.State == MEM_RESERVE
         DebPrint (("mmap_realloc: VirtualQuery error = %ld\n",
                   GetLastError ()));
       /* If there is enough room in the current reserved area, then
         commit more pages as needed.  */
       if (m2.State == MEM_RESERVE
+         && m2.AllocationBase == memInfo.AllocationBase
          && nbytes <= memInfo.RegionSize + m2.RegionSize)
        {
          void *p;
 
          && nbytes <= memInfo.RegionSize + m2.RegionSize)
        {
          void *p;
 
-         p = VirtualAlloc (*var + memInfo.RegionSize,
-                           nbytes - memInfo.RegionSize,
-                           MEM_COMMIT, PAGE_READWRITE);
+         p = VirtualAlloc (*var, nbytes, MEM_COMMIT, PAGE_READWRITE);
          if (!p /* && GetLastError() != ERROR_NOT_ENOUGH_MEMORY */)
            {
          if (!p /* && GetLastError() != ERROR_NOT_ENOUGH_MEMORY */)
            {
-             DebPrint (("realloc enlarge: VirtualAlloc error %ld\n",
+             DebPrint (("realloc enlarge: VirtualAlloc (%p + %I64x, %I64x) error %ld\n",
+                        *var, (uint64_t)memInfo.RegionSize,
+                        (uint64_t)(nbytes - memInfo.RegionSize),
                         GetLastError ()));
                         GetLastError ()));
-             errno = ENOMEM;
+             DebPrint (("next region: %p %p %I64x %x\n", m2.BaseAddress,
+                        m2.AllocationBase, (uint64_t)m2.RegionSize,
+                        m2.AllocationProtect));
            }
            }
+         else
+           return *var;
+       }
+      /* Else we must actually enlarge the block by allocating a new
+        one and copying previous contents from the old to the new one.  */
+      old_ptr = *var;
+
+      if (mmap_alloc (var, nbytes))
+       {
+         CopyMemory (*var, old_ptr, memInfo.RegionSize);
+         mmap_free (&old_ptr);
          return *var;
        }
       else
        {
          return *var;
        }
       else
        {
-         /* Else we must actually enlarge the block by allocating a
-            new one and copying previous contents from the old to the
-            new one.  */
-         void *old_ptr = *var;
-
-         if (mmap_alloc (var, nbytes))
-           {
-             CopyMemory (*var, old_ptr, memInfo.RegionSize);
-             mmap_free (&old_ptr);
-             return *var;
-           }
-         else
-           {
-             /* We failed to enlarge the buffer.  */
-             *var = old_ptr;
-             return NULL;
-           }
+         /* We failed to reallocate the buffer.  */
+         *var = old_ptr;
+         return NULL;
        }
     }
 
        }
     }
 
@@ -670,7 +759,7 @@ mmap_realloc (void **var, size_t nbytes)
         {
           /* Let's give some memory back to the system and release
             some pages.  */
         {
           /* Let's give some memory back to the system and release
             some pages.  */
-          void *old_ptr = *var;
+          old_ptr = *var;
 
          if (mmap_alloc (var, nbytes))
             {
 
          if (mmap_alloc (var, nbytes))
             {
@@ -689,7 +778,7 @@ mmap_realloc (void **var, size_t nbytes)
         }
 
       /* We still can decommit pages.  */
         }
 
       /* We still can decommit pages.  */
-      if (VirtualFree (*var + nbytes + get_page_size(),
+      if (VirtualFree ((char *)*var + nbytes + get_page_size(),
                       memInfo.RegionSize - nbytes - get_page_size(),
                       MEM_DECOMMIT) == 0)
         DebPrint (("mmap_realloc: VirtualFree error %ld\n", GetLastError ()));
                       memInfo.RegionSize - nbytes - get_page_size(),
                       MEM_DECOMMIT) == 0)
         DebPrint (("mmap_realloc: VirtualFree error %ld\n", GetLastError ()));