Use mmap(2) emulation for buffer text on MS-Windows.

src/Makefile.in (C_HEAP_SWITCH): Get the predefined heap size from
 configure.
 (ADDSECTION, MINGW_TEMACS_POST_LINK): Remove, no longer used.
 src/lisp.h (NONPOINTER_BITS): Modify the condition to define to zero
 for MinGW, since it no longer uses gmalloc.
 src/buffer.c: Do not define mmap allocations functions for Windows.
 Remove mmap_find which is unused. Remove mmap_set_vars which does
 nothing useful.
 [WINDOWSNT]: Include w32heap.h.
 (init_buffer): Always allocate new memory for buffers.
 src/emacs.c: Remove mmap_set_vars calls.
 src/image.c (free_image): Undef free for Windows because it is
 redirected to our private version.
 src/unexw32.c (COPY_PROC_CHUNK): Use %p format for 64bits
 compatibility.
 (copy_executable_and_dump_data): Remove dumping the heap section.
 (unexec): Restore using_dynamic_heap after dumping.
 src/w32heap.c (dumped_data_commit, malloc_after_dump)
 (malloc_before_dump, realloc_after_dump, realloc_before_dump)
 (free_after_dump, free_before_dump, mmap_alloc, mmap_realloc)
 (mmap_free): New functions.
 src/w32heap.h: Declare dumped_data and mmap_* function prototypes.

 nt/inc/ms-w32.h: Switch to the system heap allocation scheme
 instead of GNU malloc and ralloc.
 nt/inc/sys/mman.h: New file.
 nt/INSTALL: Update for the new build requirements.

 etc/NEWS: Mention build changes on MS-Windows.

 configure.ac (C_HEAP_SWITCH) define for different values of
 dumped heap size depending on 32/64bits arch on Windows.
 Don't check for pthreads.h on MinGW32/64, it gets in the way.
 Use mmap(2) for buffers and system malloc for MinGW32/64.
This commit is contained in:
Fabrice Popineau 2014-05-27 20:31:17 +03:00 committed by Eli Zaretskii
parent 0da7d35c67
commit 587fd086a0
17 changed files with 740 additions and 362 deletions

View file

@ -1,3 +1,10 @@
2014-05-27 Fabrice Popineau <fabrice.popineau@gmail.com>
* configure.ac (C_HEAP_SWITCH) define for different values of
dumped heap size depending on 32/64bits arch on Windows.
Don't check for pthreads.h on MinGW32/64, it gets in the way.
Use mmap(2) for buffers and system malloc for MinGW32/64.
2014-05-27 Paul Eggert <eggert@cs.ucla.edu>
Merge from gnulib, incorporating:

View file

@ -1973,7 +1973,7 @@ doug_lea_malloc=$emacs_cv_var_doug_lea_malloc
system_malloc=$emacs_cv_sanitize_address
case "$opsys" in
## darwin ld insists on the use of malloc routines in the System framework.
darwin|sol2-10) system_malloc=yes ;;
darwin|mingw32|sol2-10) system_malloc=yes ;;
esac
GMALLOC_OBJ=
@ -2020,7 +2020,7 @@ if test "$doug_lea_malloc" = "yes" ; then
## #ifdef DOUG_LEA_MALLOC; #undef REL_ALLOC; #endif
## Does the AC_FUNC_MMAP test below make this check unnecessary?
case "$opsys" in
gnu*) REL_ALLOC=no ;;
mingw32|gnu*) REL_ALLOC=no ;;
esac
fi
@ -2030,7 +2030,7 @@ fi
use_mmap_for_buffers=no
case "$opsys" in
cygwin|freebsd|irix6-5) use_mmap_for_buffers=yes ;;
cygwin|mingw32|freebsd|irix6-5) use_mmap_for_buffers=yes ;;
esac
AC_FUNC_MMAP
@ -2046,6 +2046,7 @@ AC_CHECK_LIB(Xbsd, main, LD_SWITCH_X_SITE="$LD_SWITCH_X_SITE -lXbsd")
dnl Check for the POSIX thread library.
LIB_PTHREAD=
if test "$opsys" != "mingw32"; then
AC_CHECK_HEADERS_ONCE(pthread.h)
if test "$ac_cv_header_pthread_h"; then
dnl gmalloc.c uses pthread_atfork, which is not available on older-style
@ -2066,6 +2067,7 @@ if test "$ac_cv_header_pthread_h"; then
LIBS=$OLD_LIBS
fi
AC_SUBST([LIB_PTHREAD])
fi
dnl Check for need for bigtoc support on IBM AIX
@ -4817,11 +4819,9 @@ case "$opsys" in
gnu*) LD_SWITCH_SYSTEM_TEMACS="\$(LD_SWITCH_X_SITE_RPATH)" ;;
mingw32)
## MinGW64 does not prepend an underscore to symbols, so we must
## pass a different -entry switch to linker. FIXME: It is better
## to make the entry points the same by changing unexw32.c.
## Is it any better under MinGW64 to relocate emacs into higher addresses?
case "$canonical" in
x86_64-*-*) LD_SWITCH_SYSTEM_TEMACS="-Wl,-stack,0x00800000 -Wl,-heap,0x00100000 -Wl,-image-base,0x01000000 -Wl,-entry,__start -Wl,-Map,./temacs.map" ;;
x86_64-*-*) LD_SWITCH_SYSTEM_TEMACS="-Wl,-stack,0x00800000 -Wl,-heap,0x00100000 -Wl,-image-base,0x400000000 -Wl,-entry,__start -Wl,-Map,./temacs.map" ;;
*) LD_SWITCH_SYSTEM_TEMACS="-Wl,-stack,0x00800000 -Wl,-heap,0x00100000 -Wl,-image-base,0x01000000 -Wl,-entry,__start -Wl,-Map,./temacs.map" ;;
esac
;;
@ -4845,20 +4845,20 @@ AC_SUBST(LD_SWITCH_SYSTEM_TEMACS)
## MinGW-specific post-link processing of temacs.
TEMACS_POST_LINK=":"
ADDSECTION=
EMACS_HEAPSIZE=
C_HEAP_SWITCH=
if test "${opsys}" = "mingw32"; then
TEMACS_POST_LINK="\$(MINGW_TEMACS_POST_LINK)"
ADDSECTION="../nt/addsection\$(EXEEXT)"
## Preload heap size of temacs.exe in MB.
case "$canonical" in
x86_64-*-*) EMACS_HEAPSIZE=42 ;;
*) EMACS_HEAPSIZE=27 ;;
x86_64-*-*) C_HEAP_SWITCH="-DHEAPSIZE=18" ;;
*) C_HEAP_SWITCH="-DHEAPSIZE=10" ;;
esac
fi
AC_SUBST(ADDSECTION)
AC_SUBST(TEMACS_POST_LINK)
AC_SUBST(EMACS_HEAPSIZE)
AC_SUBST(C_HEAP_SWITCH)
## Common for all window systems
if test "$window_system" != "none"; then

View file

@ -1,3 +1,7 @@
2014-05-27 Fabrice Popineau <fabrice.popineau@gmail.com>
* NEWS: Mention build changes on MS-Windows.
2014-05-26 Paul Eggert <eggert@cs.ucla.edu>
Specify coding if Latin-1 Emacs would misinterpret (Bug#17575).

View file

@ -40,6 +40,11 @@ or by sticking with Emacs 24.4.
** The configure option `--with-pkg-config-prog' has been removed.
Use './configure PKG_CONFIG=/full/name/of/pkg-config' if you need to.
---
** Building Emacs for MS-Windows requires at least Windows XP
or Windows Server 2003. The built binaries still run on all versions
of Windows starting with Windows 9X.
* Startup Changes in Emacs 24.5

View file

@ -1,3 +1,12 @@
2014-05-27 Fabrice Popineau <fabrice.popineau@gmail.com>
* inc/ms-w32.h: Switch to the system heap allocation scheme
instead of GNU malloc and ralloc.
* inc/sys/mman.h: New file.
* INSTALL: Update for the new build requirements.
2014-05-17 Paul Eggert <eggert@cs.ucla.edu>
Assume C99 or later (Bug#17487).

View file

@ -5,9 +5,9 @@
See the end of the file for license conditions.
The MSYS/MinGW build described here is supported on versions of
Windows starting with Windows 2000 and newer. Windows 9X are not
supported (but the Emacs binary produced by this build will run on
Windows 9X as well).
Windows starting with Windows XP and newer. Building on Windows 2000
and Windows 9X is not supported (but the Emacs binary produced by this
build will run on Windows 9X and newer systems).
Do not use this recipe with Cygwin. For building on Cygwin, use the
normal installation instructions, ../INSTALL.
@ -389,9 +389,10 @@ Windows 9X as well).
Where should the build process find the source code? /path/to/emacs/sources
What compiler should emacs be built with? gcc -std=gnu99 -O0 -g3
Should Emacs use the GNU version of malloc? yes
Should Emacs use a relocating allocator for buffers? yes
Should Emacs use mmap(2) for buffer allocation? no
Should Emacs use the GNU version of malloc? no
(The GNU allocators don't work with this system configuration.)
Should Emacs use a relocating allocator for buffers? no
Should Emacs use mmap(2) for buffer allocation? yes
What window system should Emacs use? w32
What toolkit should Emacs use? none
Where do we find X Windows header files? NONE
@ -401,13 +402,16 @@ Windows 9X as well).
Does Emacs use -ljpeg? yes
Does Emacs use -ltiff? yes
Does Emacs use a gif library? yes
Does Emacs use -lpng? yes
Does Emacs use -lrsvg-2? no
Does Emacs use a png library? yes
Does Emacs use -lrsvg-2? yes
Does Emacs use imagemagick? no
Does Emacs support sound? no
Does Emacs use -lgpm? no
Does Emacs use -ldbus? no
Does Emacs use -lgconf? no
Does Emacs use GSettings? no
Does Emacs use a file notification library? yes (w32)
Does Emacs use access control lists? yes
Does Emacs use -lselinux? no
Does Emacs use -lgnutls? yes
Does Emacs use -lxml2? yes
@ -415,6 +419,7 @@ Windows 9X as well).
Does Emacs use -lm17n-flt? no
Does Emacs use -lotf? no
Does Emacs use -lxft? no
Does Emacs directly use zlib? yes
Does Emacs use toolkit scroll bars? yes
You are almost there, hang on.

View file

@ -140,6 +140,7 @@ extern char *getenv ();
in its system headers, and is not really compatible with values
lower than 0x0500, so leave it alone. */
#ifndef _W64
# undef _WIN32_WINNT
# define _WIN32_WINNT 0x0400
#endif
@ -427,20 +428,36 @@ extern char *get_emacs_configuration_options (void);
#define _WINSOCK_H
/* Defines size_t and alloca (). */
#ifdef emacs
#define malloc e_malloc
#define free e_free
#define realloc e_realloc
#define calloc e_calloc
#endif
#include <stdlib.h>
#include <sys/stat.h>
#ifdef _MSC_VER
#define alloca _alloca
#else
#include <malloc.h>
#endif
#include <stdlib.h>
#include <sys/stat.h>
#ifdef emacs
typedef void * (* malloc_fn)(size_t);
typedef void * (* realloc_fn)(void *, size_t);
typedef void (* free_fn)(void *);
extern void *malloc_before_dump(size_t);
extern void *realloc_before_dump(void *, size_t);
extern void free_before_dump(void *);
extern void *malloc_after_dump(size_t);
extern void *realloc_after_dump(void *, size_t);
extern void free_after_dump(void *);
extern malloc_fn the_malloc_fn;
extern realloc_fn the_realloc_fn;
extern free_fn the_free_fn;
#define malloc(size) (*the_malloc_fn)(size)
#define free(ptr) (*the_free_fn)(ptr)
#define realloc(ptr, size) (*the_realloc_fn)(ptr, size)
#endif
/* Define for those source files that do not include enough NT system files. */
#ifndef NULL

31
nt/inc/sys/mman.h Normal file
View file

@ -0,0 +1,31 @@
/*
* sys/mman.h
* mman-win32
*/
#ifndef _SYS_MMAN_H_
#define _SYS_MMAN_H_
#include <sys/types.h>
#ifdef __cplusplus
extern "C" {
#endif
/* We need MAP_ANON in src/buffer.c */
#define MAP_FILE 0
#define MAP_SHARED 1
#define MAP_PRIVATE 2
#define MAP_TYPE 0xf
#define MAP_FIXED 0x10
#define MAP_ANONYMOUS 0x20
#define MAP_ANON MAP_ANONYMOUS
#define MAP_FAILED ((void *)-1)
#ifdef __cplusplus
};
#endif
#endif /* _SYS_MMAN_H_ */

View file

@ -1,3 +1,35 @@
2014-05-27 Fabrice Popineau <fabrice.popineau@gmail.com>
* Makefile.in (C_HEAP_SWITCH): Get the predefined heap size from
configure.
(ADDSECTION, MINGW_TEMACS_POST_LINK): Remove, no longer used.
* lisp.h (NONPOINTER_BITS): Modify the condition to define to zero
for MinGW, since it no longer uses gmalloc.
* buffer.c: Do not define mmap allocations functions for Windows.
Remove mmap_find which is unused. Remove mmap_set_vars which does
nothing useful.
[WINDOWSNT]: Include w32heap.h.
(init_buffer): Always allocate new memory for buffers.
* emacs.c: Remove mmap_set_vars calls.
* image.c (free_image): Undef free for Windows because it is
redirected to our private version.
* unexw32.c (COPY_PROC_CHUNK): Use %p format for 64bits
compatibility.
(copy_executable_and_dump_data): Remove dumping the heap section.
(unexec): Restore using_dynamic_heap after dumping.
* w32heap.c (dumped_data_commit, malloc_after_dump)
(malloc_before_dump, realloc_after_dump, realloc_before_dump)
(free_after_dump, free_before_dump, mmap_alloc, mmap_realloc)
(mmap_free): New functions.
* w32heap.h: Declare dumped_data and mmap_* function prototypes.
2014-05-27 Paul Eggert <eggert@cs.ucla.edu>
* image.c (imagemagick_load_image): Use MagickRealType for local

View file

@ -86,6 +86,9 @@ PNG_CFLAGS=@PNG_CFLAGS@
## something similar. This is normally set by configure.
C_SWITCH_X_SITE=@C_SWITCH_X_SITE@
## Set Emacs dumped heap size for Windows NT
C_HEAP_SWITCH=@C_HEAP_SWITCH@
## Define LD_SWITCH_X_SITE to contain any special flags your loader
## may need to deal with X Windows. For instance, if your X libraries
## aren't in a place that your loader can find on its own, you might
@ -300,11 +303,7 @@ RUN_TEMACS = ./temacs
## Invoke ../nt/addsection for MinGW, ":" elsewhere.
TEMACS_POST_LINK = @TEMACS_POST_LINK@
ADDSECTION = @ADDSECTION@
EMACS_HEAPSIZE = @EMACS_HEAPSIZE@
MINGW_TEMACS_POST_LINK = \
mv temacs$(EXEEXT) temacs.tmp; \
../nt/addsection temacs.tmp temacs$(EXEEXT) EMHEAP $(EMACS_HEAPSIZE)
UNEXEC_OBJ = @UNEXEC_OBJ@
@ -326,7 +325,7 @@ MKDEPDIR=@MKDEPDIR@
##
## FIXME? MYCPPFLAGS only referenced in etc/DEBUG.
ALL_CFLAGS=-Demacs $(MYCPPFLAGS) -I. -I$(srcdir) \
-I$(lib) -I$(srcdir)/../lib \
-I$(lib) -I$(srcdir)/../lib $(C_HEAP_SWITCH) \
$(C_SWITCH_MACHINE) $(C_SWITCH_SYSTEM) $(C_SWITCH_X_SITE) \
$(GNUSTEP_CFLAGS) $(CFLAGS_SOUND) $(RSVG_CFLAGS) $(IMAGEMAGICK_CFLAGS) \
$(PNG_CFLAGS) $(LIBXML2_CFLAGS) $(DBUS_CFLAGS) \

View file

@ -41,6 +41,10 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#include "keymap.h"
#include "frame.h"
#ifdef WINDOWSNT
#include "w32heap.h" /* for mmap_* */
#endif
struct buffer *current_buffer; /* The current buffer. */
/* First buffer in chain of all buffers (in reverse order of creation).
@ -4632,7 +4636,8 @@ evaporate_overlays (ptrdiff_t pos)
Allocation with mmap
***********************************************************************/
#ifdef USE_MMAP_FOR_BUFFERS
/* Note: WINDOWSNT implements this stuff on w32heap.c. */
#if defined USE_MMAP_FOR_BUFFERS && !defined WINDOWSNT
#include <sys/mman.h>
@ -4774,36 +4779,6 @@ mmap_init (void)
mmap_page_size = getpagesize ();
}
/* Return a region overlapping address range START...END, or null if
none. END is not including, i.e. the last byte in the range
is at END - 1. */
static struct mmap_region *
mmap_find (void *start, void *end)
{
struct mmap_region *r;
char *s = start, *e = end;
for (r = mmap_regions; r; r = r->next)
{
char *rstart = (char *) r;
char *rend = rstart + r->nbytes_mapped;
if (/* First byte of range, i.e. START, in this region? */
(s >= rstart && s < rend)
/* Last byte of range, i.e. END - 1, in this region? */
|| (e > rstart && e <= rend)
/* First byte of this region in the range? */
|| (rstart >= s && rstart < e)
/* Last byte of this region in the range? */
|| (rend > s && rend <= e))
break;
}
return r;
}
/* Unmap a region. P is a pointer to the start of the user-araa of
the region. */
@ -4880,38 +4855,6 @@ mmap_enlarge (struct mmap_region *r, int npages)
}
/* Set or reset variables holding references to mapped regions.
If not RESTORE_P, set all variables to null. If RESTORE_P, set all
variables to the start of the user-areas of mapped regions.
This function is called from Fdump_emacs to ensure that the dumped
Emacs doesn't contain references to memory that won't be mapped
when Emacs starts. */
void
mmap_set_vars (bool restore_p)
{
struct mmap_region *r;
if (restore_p)
{
mmap_regions = mmap_regions_1;
mmap_fd = mmap_fd_1;
for (r = mmap_regions; r; r = r->next)
*r->var = MMAP_USER_AREA (r);
}
else
{
for (r = mmap_regions; r; r = r->next)
*r->var = NULL;
mmap_regions_1 = mmap_regions;
mmap_regions = NULL;
mmap_fd_1 = mmap_fd;
mmap_fd = -1;
}
}
/* Allocate a block of storage large enough to hold NBYTES bytes of
data. A pointer to the data is returned in *VAR. VAR is thus the
address of some variable which will use the data area.

View file

@ -2155,13 +2155,8 @@ You must run Emacs in batch mode in order to dump it. */)
malloc_state_ptr = malloc_get_state ();
#endif
#ifdef USE_MMAP_FOR_BUFFERS
mmap_set_vars (0);
#endif
unexec (SSDATA (filename), !NILP (symfile) ? SSDATA (symfile) : 0);
#ifdef USE_MMAP_FOR_BUFFERS
mmap_set_vars (1);
#endif
#ifdef DOUG_LEA_MALLOC
free (malloc_state_ptr);
#endif

View file

@ -998,6 +998,11 @@ free_image (struct frame *f, struct image *img)
c->images[img->id] = NULL;
/* Windows NT redefines 'free', but in this file, we need to
avoid the redefinition. */
#ifdef WINDOWSNT
#undef free
#endif
/* Free resources, then free IMG. */
img->type->free (f, img);
xfree (img);
@ -6453,7 +6458,6 @@ jpeg_file_src (j_decompress_ptr cinfo, FILE *fp)
src->mgr.next_input_byte = NULL;
}
/* Load image IMG for use on frame F. Patterned after example.c
from the JPEG lib. */

View file

@ -72,7 +72,7 @@ DEFINE_GDB_SYMBOL_END (GCTYPEBITS)
2. We know malloc returns a multiple of 8. */
#if (defined alignas \
&& (defined GNU_MALLOC || defined DOUG_LEA_MALLOC || defined __GLIBC__ \
|| defined DARWIN_OS || defined __sun))
|| defined DARWIN_OS || defined __sun || defined __MINGW32__))
# define NONPOINTER_BITS 0
#else
# define NONPOINTER_BITS GCTYPEBITS

View file

@ -83,8 +83,6 @@ PCHAR bss_start_static = 0;
DWORD_PTR bss_size_static = 0;
DWORD_PTR extra_bss_size_static = 0;
PIMAGE_SECTION_HEADER heap_section;
/* MinGW64 doesn't add a leading underscore to external symbols,
whereas configure.ac sets up LD_SWITCH_SYSTEM_TEMACS to force the
entry point at __start, with two underscores. */
@ -475,8 +473,6 @@ get_section_info (file_data *p_infile)
bss_section_static = 0;
extra_bss_size_static = 0;
}
heap_section = rva_to_section (PTR_TO_RVA (get_heap_start ()), nt_header);
}
@ -518,9 +514,11 @@ copy_executable_and_dump_data (file_data *p_infile,
if (verbose) \
{ \
printf ("%s\n", (message)); \
printf ("\t0x%08x Address in process.\n", s); \
printf ("\t0x%08x Offset in output file.\n", dst - p_outfile->file_base); \
printf ("\t0x%08x Size in bytes.\n", count); \
printf ("\t0x%p Address in process.\n", s); \
printf ("\t0x%p Base output file.\n", p_outfile->file_base); \
printf ("\t0x%p Offset in output file.\n", dst - p_outfile->file_base); \
printf ("\t0x%p Address in output file.\n", dst); \
printf ("\t0x%p Size in bytes.\n", count); \
} \
memcpy (dst, s, count); \
dst += count; \
@ -629,34 +627,6 @@ copy_executable_and_dump_data (file_data *p_infile,
dst_section->Characteristics &= ~IMAGE_SCN_CNT_UNINITIALIZED_DATA;
dst_section->Characteristics |= IMAGE_SCN_CNT_INITIALIZED_DATA;
}
if (section == heap_section)
{
DWORD_PTR heap_start = (DWORD_PTR) get_heap_start ();
DWORD_PTR heap_size = get_committed_heap_size ();
/* Dump the used portion of the predump heap, adjusting the
section's size to the appropriate size. */
dst = dst_save
+ RVA_TO_SECTION_OFFSET (PTR_TO_RVA (heap_start), dst_section);
COPY_PROC_CHUNK ("Dumping heap...", heap_start, heap_size,
be_verbose);
ROUND_UP_DST (dst_nt_header->OptionalHeader.FileAlignment);
dst_section->PointerToRawData = PTR_TO_OFFSET (dst_save, p_outfile);
/* Determine new size of raw data area. */
dst = max (dst, dst_save + dst_section->SizeOfRawData);
dst_section->SizeOfRawData = dst - dst_save;
/* Reduce the size of the heap section to fit (must be last
section). */
dst_nt_header->OptionalHeader.SizeOfImage -=
dst_section->Misc.VirtualSize
- ROUND_UP (dst_section->SizeOfRawData,
dst_nt_header->OptionalHeader.SectionAlignment);
dst_section->Misc.VirtualSize =
ROUND_UP (dst_section->SizeOfRawData,
dst_nt_header->OptionalHeader.SectionAlignment);
dst_section->Characteristics &= ~IMAGE_SCN_CNT_UNINITIALIZED_DATA;
dst_section->Characteristics |= IMAGE_SCN_CNT_INITIALIZED_DATA;
}
/* Align the section's raw data area. */
ROUND_UP_DST (dst_nt_header->OptionalHeader.FileAlignment);
@ -767,9 +737,6 @@ unexec (const char *new_name, const char *old_name)
printf ("Dumping from %s\n", in_filename);
printf (" to %s\n", out_filename);
/* We need to round off our heap to NT's page size. */
round_heap (get_page_size ());
/* Open the undumped executable file. */
if (!open_input_file (&in_file, in_filename))
{
@ -784,7 +751,6 @@ unexec (const char *new_name, const char *old_name)
/* The size of the dumped executable is the size of the original
executable plus the size of the heap and the size of the .bss section. */
size = in_file.size +
get_committed_heap_size () +
extra_bss_size +
extra_bss_size_static;
if (!open_output_file (&out_file, out_filename, size))
@ -799,6 +765,10 @@ unexec (const char *new_name, const char *old_name)
copy_executable_and_dump_data (&in_file, &out_file);
/* Unset it because it is plain wrong to keep it after dumping.
Malloc can still occur! */
using_dynamic_heap = FALSE;
/* Patch up header fields; profiler is picky about this. */
{
PIMAGE_DOS_HEADER dos_header;

View file

@ -1,256 +1,611 @@
/* Heap management routines for GNU Emacs on the Microsoft Windows API.
Copyright (C) 1994, 2001-2014 Free Software Foundation, Inc.
/* Heap management routines for GNU Emacs on the Microsoft Windows
API. Copyright (C) 1994, 2001-2014 Free Software Foundation, Inc.
This file is part of GNU Emacs.
This file is part of GNU Emacs.
GNU Emacs is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
GNU Emacs is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
GNU Emacs 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 for more details.
GNU Emacs 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 for more details.
You should have received a copy of the GNU General Public License
along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
You should have received a copy of the GNU General Public License
along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
/*
Geoff Voelker (voelker@cs.washington.edu) 7-29-94
Geoff Voelker (voelker@cs.washington.edu) 7-29-94
*/
/*
Heavily modified by Fabrice Popineau (fabrice.popineau@gmail.com) 28-02-2014
*/
/*
Memory allocation scheme for w32/w64:
- Buffers are mmap'ed using a very simple emulation of mmap/munmap
- During the temacs phase:
* we use a private heap declared to be stored into the `dumped_data'
* unfortunately, this heap cannot be made growable, so the size of
blocks it can allocate is limited to (0x80000 - pagesize)
* the blocks that are larger than this are allocated from the end
of the `dumped_data' array; there are not so many of them.
We use a very simple first-fit scheme to reuse those blocks.
* we check that the private heap does not cross the area used
by the bigger chunks.
- During the emacs phase:
* we create a private heap for new memory blocks
* we make sure that we never free a block that has been dumped.
Freeing a dumped block could work in principle, but may prove
unreliable if we distribute binaries of emacs.exe: MS does not
guarantee that the heap data structures are the same across all
versions of their OS, even though the API is available since XP. */
#include <config.h>
#include <stdio.h>
#include <sys/mman.h>
#include "w32common.h"
#include "w32heap.h"
#include "lisp.h" /* for VALMASK */
#define RVA_TO_PTR(rva) ((unsigned char *)((DWORD_PTR)(rva) + (DWORD_PTR)GetModuleHandle (NULL)))
/* We chose to leave those declarations here. They are used only in
this file. The RtlCreateHeap is available since XP. It is located
in ntdll.dll and is available with the DDK. People often
complained that HeapCreate doesn't offer the ability to create a
heap at a given place, which we need here, and which RtlCreateHeap
provides. We reproduce here the definitions available with the
DDK. */
/* Emulate getpagesize. */
int
getpagesize (void)
{
return sysinfo_cache.dwPageSize;
}
typedef PVOID (WINAPI * RtlCreateHeap_Proc) (
/* _In_ */ ULONG Flags,
/* _In_opt_ */ PVOID HeapBase,
/* _In_opt_ */ SIZE_T ReserveSize,
/* _In_opt_ */ SIZE_T CommitSize,
/* _In_opt_ */ PVOID Lock,
/* _In_opt_ */ PVOID Parameters
);
typedef LONG NTSTATUS;
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;
SIZE_T SegmentReserve;
SIZE_T SegmentCommit;
SIZE_T DeCommitFreeBlockThreshold;
SIZE_T DeCommitTotalFreeThreshold;
SIZE_T MaximumAllocationSize;
SIZE_T VirtualMemoryThreshold;
SIZE_T InitialCommit;
SIZE_T InitialReserve;
PRTL_HEAP_COMMIT_ROUTINE CommitRoutine;
SIZE_T Reserved[ 2 ];
} RTL_HEAP_PARAMETERS, *PRTL_HEAP_PARAMETERS;
/* We reserve space for dumping emacs lisp byte-code inside a static
array. By storing it in an array, the generic mechanism in
unexecw32.c will be able to dump it without the need to add a
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
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
be freed anyway), and we use a new private heap for all new
allocations. */
unsigned char dumped_data[DUMPED_HEAP_SIZE];
/* Info for managing our preload heap, which is essentially a fixed size
data area in the executable. */
PIMAGE_SECTION_HEADER preload_heap_section;
/* Info for keeping track of our heap. */
data area in the executable. */
/* Info for keeping track of our heap. */
unsigned char *data_region_base = NULL;
unsigned char *data_region_end = NULL;
unsigned char *real_data_region_end = NULL;
size_t reserved_heap_size = 0;
static DWORD_PTR committed = 0;
/* The start of the data segment. */
unsigned char *
get_data_start (void)
/* The maximum block size that can be handled by a non-growable w32
heap is limited by the MaxBlockSize value below.
This point deserves and explanation.
The W32 heap allocator can be used for a growable
heap or a non-growable one.
A growable heap is not compatible with a fixed base address for the
heap. Only a non-growable one is. One drawback of non-growable
heaps is that they can hold only objects smaller than a certain
size (the one defined below). Most of the largest blocks are GC'ed
before dumping. In any case and to be safe, we implement a simple
first-fit allocation algorithm starting at the end of the
dumped_data[] array like depicted below:
----------------------------------------------
| | | |
| Private heap |-> <-| Big chunks |
| | | |
----------------------------------------------
^ ^ ^
dumped_data dumped_data bc_limit
+ committed
*/
#define HEAP_ENTRY_SHIFT 3
#define PAGE_SIZE 0x1000
#define MaxBlockSize (0x80000 - PAGE_SIZE)
#define MAX_BLOCKS 0x40
static struct
{
return data_region_base;
}
unsigned char *address;
size_t size;
DWORD occupied;
} blocks[MAX_BLOCKS];
/* The end of the data segment. */
unsigned char *
get_data_end (void)
static DWORD blocks_number = 0;
static unsigned char *bc_limit;
/* Handle for the private heap:
- inside the dumped_data[] array before dump,
- outside of it after dump.
*/
HANDLE heap = NULL;
/* We redirect the standard allocation functions. */
malloc_fn the_malloc_fn;
realloc_fn the_realloc_fn;
free_fn the_free_fn;
/* It doesn't seem to be useful to allocate from a file mapping.
It would be if the memory was shared.
http://stackoverflow.com/questions/307060/what-is-the-purpose-of-allocating-pages-in-the-pagefile-with-createfilemapping */
/* This is the function to commit memory when the heap allocator
claims for new memory. Before dumping, we allocate space
from the fixed size dumped_data[] array.
*/
NTSTATUS NTAPI
dumped_data_commit (PVOID Base, PVOID *CommitAddress, PSIZE_T CommitSize)
{
return data_region_end;
}
/* This is used before dumping.
#if !USE_LSB_TAG
static char *
allocate_heap (void)
{
/* Try to get as much as possible of the address range from the end of
the preload heap section up to the usable address limit. Since GNU
malloc can handle gaps in the memory it gets from sbrk, we can
simply set the sbrk pointer to the base of the new heap region. */
DWORD_PTR base =
ROUND_UP ((RVA_TO_PTR (preload_heap_section->VirtualAddress)
+ preload_heap_section->Misc.VirtualSize),
get_allocation_unit ());
DWORD_PTR end = ((unsigned __int64)1) << VALBITS; /* 256MB */
void *ptr = NULL;
while (!ptr && (base < end))
The private heap is stored at dumped_data[] address.
We commit contiguous areas of the dumped_data array
as requests arrive. */
*CommitAddress = data_region_base + committed;
committed += *CommitSize;
if (((unsigned char *)(*CommitAddress)) + *CommitSize >= bc_limit)
{
#ifdef _WIN64
reserved_heap_size = min(end - base, 0x4000000000ull); /* Limit to 256Gb */
#else
reserved_heap_size = end - base;
/* Check that the private heap area does not overlap the big
chunks area. */
fprintf(stderr,
"dumped_data_commit: memory exhausted.\nEnlarge dumped_data[]!\n");
exit (-1);
}
return 0;
}
/* Heap creation. */
/* Under MinGW32, we want to turn on Low Fragmentation Heap for XP.
MinGW32 lacks those definitions. */
#ifndef _W64
typedef enum _HEAP_INFORMATION_CLASS {
HeapCompatibilityInformation
} HEAP_INFORMATION_CLASS;
typedef WINBASEAPI BOOL (WINAPI * HeapSetInformation_Proc)(HANDLE,HEAP_INFORMATION_CLASS,PVOID,SIZE_T);
#endif
ptr = VirtualAlloc ((void *) base,
get_reserved_heap_size (),
MEM_RESERVE,
PAGE_NOACCESS);
base += 0x00100000; /* 1MB increment */
}
return ptr;
}
#else /* USE_LSB_TAG */
static char *
allocate_heap (void)
{
#ifdef _WIN64
size_t size = 0x4000000000ull; /* start by asking for 32GB */
#else
/* We used to start with 2GB here, but on Windows 7 that would leave
too little room in the address space for threads started by
Windows on our behalf, e.g. when we pop up the file selection
dialog. */
size_t size = 0x68000000; /* start by asking for 1.7GB */
#endif
void *ptr = NULL;
while (!ptr && size > 0x00100000)
{
reserved_heap_size = size;
ptr = VirtualAlloc (NULL,
get_reserved_heap_size (),
MEM_RESERVE,
PAGE_NOACCESS);
size -= 0x00800000; /* if failed, decrease request by 8MB */
}
return ptr;
}
#endif /* USE_LSB_TAG */
/* Emulate Unix sbrk. Note that ralloc.c expects the return value to
be the address of the _start_ (not end) of the new block in case of
success, and zero (not -1) in case of failure. */
void *
sbrk (ptrdiff_t increment)
{
void *result;
ptrdiff_t size = increment;
result = data_region_end;
/* If size is negative, shrink the heap by decommitting pages. */
if (size < 0)
{
ptrdiff_t new_size;
unsigned char *new_data_region_end;
size = -size;
/* Sanity checks. */
if ((data_region_end - size) < data_region_base)
return NULL;
/* We can only decommit full pages, so allow for
partial deallocation [cga]. */
new_data_region_end = (data_region_end - size);
new_data_region_end = (unsigned char *)
((DWORD_PTR) (new_data_region_end + syspage_mask) & ~syspage_mask);
new_size = real_data_region_end - new_data_region_end;
real_data_region_end = new_data_region_end;
if (new_size > 0)
{
/* Decommit size bytes from the end of the heap. */
if (using_dynamic_heap
&& !VirtualFree (real_data_region_end, new_size, MEM_DECOMMIT))
return NULL;
}
data_region_end -= size;
}
/* If size is positive, grow the heap by committing reserved pages. */
else if (size > 0)
{
/* Sanity checks. */
if ((data_region_end + size) >
(data_region_base + get_reserved_heap_size ()))
return NULL;
/* Commit more of our heap. */
if (using_dynamic_heap
&& VirtualAlloc (data_region_end, size, MEM_COMMIT,
PAGE_READWRITE) == NULL)
return NULL;
data_region_end += size;
/* We really only commit full pages, so record where
the real end of committed memory is [cga]. */
real_data_region_end = (unsigned char *)
((DWORD_PTR) (data_region_end + syspage_mask) & ~syspage_mask);
}
return result;
}
/* Initialize the internal heap variables used by sbrk. When running in
preload phase (ie. in the undumped executable), we rely entirely on a
fixed size heap section included in the .exe itself; this is
preserved during dumping, and truncated to the size actually used.
When running in the dumped executable, we reserve as much as possible
of the address range that is addressable by Lisp object pointers, to
supplement what is left of the preload heap. Although we cannot rely
on the dynamically allocated arena being contiguous with the static
heap area, it is not a problem because sbrk can pretend that the gap
was allocated by something else; GNU malloc detects when there is a
jump in the sbrk values, and starts a new heap block. */
void
init_heap (void)
{
PIMAGE_DOS_HEADER dos_header;
PIMAGE_NT_HEADERS nt_header;
dos_header = (PIMAGE_DOS_HEADER) RVA_TO_PTR (0);
nt_header = (PIMAGE_NT_HEADERS) (((DWORD_PTR) dos_header) +
dos_header->e_lfanew);
preload_heap_section = find_section ("EMHEAP", nt_header);
if (using_dynamic_heap)
{
data_region_base = allocate_heap ();
if (!data_region_base)
{
printf ("Error: Could not reserve dynamic heap area.\n");
exit (1);
}
unsigned long enable_lfh = 2;
#if !USE_LSB_TAG
/* Ensure that the addresses don't use the upper tag bits since
the Lisp type goes there. */
if (((DWORD_PTR) data_region_base & ~VALMASK) != 0)
{
printf ("Error: The heap was allocated in upper memory.\n");
exit (1);
}
#endif
/* After dumping, use a new private heap. We explicitly enable
the low fragmentation heap here, for the sake of pre Vista
versions. Note: this will harnlessly fail on Vista and
later, whyere the low fragmentation heap is enabled by
default. It will also fail on pre-Vista versions when Emacs
is run under a debugger; set _NO_DEBUG_HEAP=1 in the
environment before starting GDB to get low fragmentation heap
on XP and older systems, for the price of losing "certain
heap debug options"; for the details see
http://msdn.microsoft.com/en-us/library/windows/desktop/aa366705%28v=vs.85%29.aspx. */
data_region_end = data_region_base;
real_data_region_end = data_region_end;
/* Create the private heap. */
heap = HeapCreate(0, 0, 0);
#ifndef _W64
/* Set the low-fragmentation heap for OS before XP and Windows
Server 2003. */
HMODULE hm_kernel32dll = LoadLibrary("kernel32.dll");
HeapSetInformation_Proc s_pfn_Heap_Set_Information = (HeapSetInformation_Proc) GetProcAddress(hm_kernel32dll, "HeapSetInformation");
if (s_pfn_Heap_Set_Information != NULL)
if (s_pfn_Heap_Set_Information ((PVOID) heap,
HeapCompatibilityInformation,
&enable_lfh, sizeof(enable_lfh)) == 0)
DebPrint (("Enabling Low Fragmentation Heap failed\n"));
#endif
the_malloc_fn = malloc_after_dump;
the_realloc_fn = realloc_after_dump;
the_free_fn = free_after_dump;
}
else
{
data_region_base = RVA_TO_PTR (preload_heap_section->VirtualAddress);
data_region_end = data_region_base;
real_data_region_end = data_region_end;
reserved_heap_size = preload_heap_section->Misc.VirtualSize;
/* Find the RtlCreateHeap function. Headers for this function
are provided with the w32 ddk, but the function is available
in ntdll.dll since XP. */
HMODULE hm_ntdll = LoadLibrary ("ntdll.dll");
RtlCreateHeap_Proc s_pfn_Rtl_Create_Heap
= (RtlCreateHeap_Proc) GetProcAddress (hm_ntdll, "RtlCreateHeap");
/* Specific parameters for the private heap. */
RTL_HEAP_PARAMETERS params;
ZeroMemory(&params, sizeof(params));
params.Length = sizeof(RTL_HEAP_PARAMETERS);
data_region_base = (unsigned char *)ROUND_UP (dumped_data, 0x1000);
data_region_end = bc_limit = dumped_data + DUMPED_HEAP_SIZE;
params.InitialCommit = committed = 0x1000;
params.InitialReserve = sizeof(dumped_data);
/* Use our own routine to commit memory from the dumped_data
array. */
params.CommitRoutine = &dumped_data_commit;
/* Create the private heap. */
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;
}
/* Update system version information to match current system. */
cache_system_info ();
}
/* Round the heap up to the given alignment. */
void
round_heap (size_t align)
#undef malloc
#undef realloc
#undef calloc
#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)
void *
malloc_after_dump (size_t size)
{
DWORD_PTR needs_to_be;
DWORD_PTR need_to_alloc;
/* Use the new private heap. */
void *p = HeapAlloc (heap, 0, size);
needs_to_be = (DWORD_PTR) ROUND_UP (get_heap_end (), align);
need_to_alloc = needs_to_be - (DWORD_PTR) get_heap_end ();
if (need_to_alloc)
sbrk (need_to_alloc);
/* After dump, keep track of the last allocated byte for sbrk(0). */
data_region_end = p + size - 1;
return p;
}
void *
malloc_before_dump (size_t size)
{
void *p;
/* Before dumping. The private heap can handle only requests for
less than MaxBlockSize. */
if (size < MaxBlockSize)
{
/* Use the private heap if possible. */
p = HeapAlloc (heap, 0, size);
}
else
{
/* Find the first big chunk that can hold the requested size. */
int i = 0;
for (i = 0; i < blocks_number; i++)
{
if (blocks[i].occupied == 0 && blocks[i].size >= size)
break;
}
if (i < blocks_number)
{
/* If found, use it. */
p = blocks[i].address;
blocks[i].occupied = TRUE;
}
else
{
/* Allocate a new big chunk from the end of the dumped_data
array. */
if (blocks_number >= MAX_BLOCKS)
{
fprintf(stderr,
"malloc_before_dump: no more big chunks available.\nEnlarge MAX_BLOCKS!\n");
exit (-1);
}
bc_limit -= size;
bc_limit = (unsigned char *)ROUND_DOWN (bc_limit, 0x10);
p = bc_limit;
blocks[blocks_number].address = p;
blocks[blocks_number].size = size;
blocks[blocks_number].occupied = TRUE;
blocks_number++;
if (bc_limit < dumped_data + committed)
{
/* Check that areas do not overlap. */
fprintf(stderr,
"malloc_before_dump: memory exhausted.\nEnlarge dumped_data[]!\n");
exit (-1);
}
}
}
return p;
}
/* Re-allocate the previously allocated block in ptr, making the new
block SIZE bytes long. */
void *
realloc_after_dump (void *ptr, size_t size)
{
void *p;
/* After dumping. */
if (FREEABLE_P (ptr))
{
/* Reallocate the block since it lies in the new heap. */
p = HeapReAlloc (heap, 0, ptr, size);
}
else
{
/* If the block lies in the dumped data, do not free it. Only
allocate a new one. */
p = HeapAlloc (heap, 0, size);
CopyMemory (p, ptr, size);
}
/* After dump, keep track of the last allocated byte for sbrk(0). */
data_region_end = p + size - 1;
return p;
}
void *
realloc_before_dump (void *ptr, size_t size)
{
void *p;
/* Before dumping. */
if (dumped_data < (unsigned char *)ptr
&& (unsigned char *)ptr < bc_limit && size <= MaxBlockSize)
p = HeapReAlloc (heap, 0, ptr, size);
else
{
/* In this case, either the new block is too large for the heap,
or the old block was already too large. In both cases,
malloc_before_dump() and free_before_dump() will take care of
reallocation. */
p = malloc_before_dump (size);
CopyMemory (p, ptr, size);
free_before_dump (ptr);
}
return p;
}
/* Free a block allocated by `malloc', `realloc' or `calloc'. */
void
free_after_dump (void *ptr)
{
/* After dumping. */
if (FREEABLE_P (ptr))
{
/* Free the block if it is in the new private heap. */
HeapFree (heap, 0, ptr);
}
}
void
free_before_dump (void *ptr)
{
/* Before dumping. */
if (dumped_data < (unsigned char *)ptr
&& (unsigned char *)ptr < bc_limit)
{
/* Free the block if it is allocated in the private heap. */
HeapFree (heap, 0, ptr);
}
else
{
/* Look for the big chunk. */
int i;
for(i = 0; i < blocks_number; i++)
{
if (blocks[i].address == ptr)
{
/* Reset block occupation if found. */
blocks[i].occupied = 0;
break;
}
/* What if the block is not found? We should trigger an
error here. */
eassert (i < blocks_number);
}
}
}
/* Emulate getpagesize. */
int
getpagesize (void)
{
return sysinfo_cache.dwPageSize;
}
void *
sbrk (ptrdiff_t increment)
{
/* The data_region_end address is the one of the last byte
allocated. The sbrk() function is not emulated at all, except
for a 0 value of its parameter. This is needed by the emacs lisp
function `memory-limit'. */
return data_region_end;
}
#define MAX_BUFFER_SIZE (512 * 1024 * 1024)
/* MMAP allocation for buffers. */
void *
mmap_alloc (void **var, size_t nbytes)
{
void *p = NULL;
/* We implement amortized allocation. We start by reserving twice
the size requested and commit only the size requested. Then
realloc could proceed and use the reserved pages, reallocating
only if needed. Buffer shrink would happen only so that we stay
in the 2x range. This is a big win when visiting compressed
files, where the final size of the buffer is not known in
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);
/* 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);
if (p != NULL)
{
/* Now, commit pages for NBYTES. */
*var = VirtualAlloc (p, nbytes, MEM_COMMIT, PAGE_READWRITE);
}
if (!p && GetLastError () != ERROR_NOT_ENOUGH_MEMORY)
DebPrint (("mmap_alloc: error %ld\n", GetLastError()));
return *var = p;
}
void
mmap_free (void **var)
{
if (*var)
{
if (VirtualFree (*var, 0, MEM_RELEASE) == 0)
DebPrint (("mmap_free: error %ld\n", GetLastError()));
*var = NULL;
}
}
void *
mmap_realloc (void **var, size_t nbytes)
{
MEMORY_BASIC_INFORMATION memInfo, m2;
if (*var == NULL)
return mmap_alloc (var, nbytes);
/* This case happens in init_buffer(). */
if (nbytes == 0)
{
mmap_free (var);
return mmap_alloc (var, 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)
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
&& nbytes <= memInfo.RegionSize + m2.RegionSize)
{
void *p;
p = VirtualAlloc (*var + memInfo.RegionSize,
nbytes - memInfo.RegionSize,
MEM_COMMIT, PAGE_READWRITE);
if (!p /* && GetLastError() != ERROR_NOT_ENOUGH_MEMORY */)
DebPrint (("realloc enlarge: VirtualAlloc error %ld\n",
GetLastError()));
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;
}
}
}
/* If we are shrinking by more than one page... */
if (memInfo.RegionSize > nbytes + getpagesize())
{
/* If we are shrinking a lot... */
if ((memInfo.RegionSize / 2) > nbytes)
{
/* Let's give some memory back to the system and release
some pages. */
void *old_ptr = *var;
if (mmap_alloc (var, nbytes))
{
CopyMemory (*var, old_ptr, nbytes);
mmap_free (&old_ptr);
return *var;
}
else
{
/* In case we fail to shrink, try to go on with the old block.
But that means there is a lot of memory pressure.
We could also decommit pages. */
*var = old_ptr;
return *var;
}
}
/* We still can decommit pages. */
if (VirtualFree (*var + nbytes + get_page_size(),
memInfo.RegionSize - nbytes - get_page_size(),
MEM_DECOMMIT) == 0)
DebPrint (("mmap_realloc: VirtualFree error %ld\n", GetLastError()));
return *var;
}
/* Not enlarging, not shrinking by more than one page. */
return *var;
}

View file

@ -27,15 +27,20 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
/*
* Heap related stuff.
*/
#define get_reserved_heap_size() reserved_heap_size
#define get_committed_heap_size() (get_data_end () - get_data_start ())
#define get_heap_start() get_data_start ()
#define get_heap_end() get_data_end ()
#define DUMPED_HEAP_SIZE (HEAPSIZE*1024*1024)
extern unsigned char dumped_data[];
extern unsigned char *get_data_start (void);
extern unsigned char *get_data_end (void);
extern size_t reserved_heap_size;
extern BOOL using_dynamic_heap;
extern BOOL using_dynamic_heap;
extern void *mmap_realloc (void **, size_t);
extern void mmap_free (void **);
extern void *mmap_alloc (void **, size_t);
/* Emulation of Unix sbrk(). */
extern void *sbrk (ptrdiff_t size);
@ -43,11 +48,8 @@ extern void *sbrk (ptrdiff_t size);
/* Initialize heap structures for sbrk on startup. */
extern void init_heap (void);
/* Round the heap to this size. */
extern void round_heap (size_t size);
/* ----------------------------------------------------------------- */
/* Useful routines for manipulating memory-mapped files. */
/* Useful routines for manipulating memory-mapped files. */
typedef struct file_data {
char *name;
@ -61,11 +63,11 @@ int open_input_file (file_data *p_file, char *name);
int open_output_file (file_data *p_file, char *name, unsigned long size);
void close_file_data (file_data *p_file);
/* Return pointer to section header for named section. */
/* Return pointer to section header for named section. */
IMAGE_SECTION_HEADER * find_section (char * name, IMAGE_NT_HEADERS * nt_header);
/* Return pointer to section header for section containing the given
relative virtual address. */
relative virtual address. */
IMAGE_SECTION_HEADER * rva_to_section (DWORD_PTR rva, IMAGE_NT_HEADERS * nt_header);
#endif /* NTHEAP_H_ */