[pypy-commit] pypy default: Found out how, in general on POSIX, we're supposed to get fresh

arigo noreply at buildbot.pypy.org
Fri Feb 21 09:26:28 CET 2014


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r69235:992e29624c5f
Date: 2014-02-21 09:25 +0100
http://bitbucket.org/pypy/pypy/changeset/992e29624c5f/

Log:	Found out how, in general on POSIX, we're supposed to get fresh
	zero-mapped pages inside an mmap. Replaces the hacks with madvise()
	or reading /dev/zero. The answer is simply to call mmap(MAP_FIXED)
	again.

diff --git a/rpython/rlib/rmmap.py b/rpython/rlib/rmmap.py
--- a/rpython/rlib/rmmap.py
+++ b/rpython/rlib/rmmap.py
@@ -55,7 +55,7 @@
     # constants, look in sys/mman.h and platform docs for the meaning
     # some constants are linux only so they will be correctly exposed outside
     # depending on the OS
-    constant_names = ['MAP_SHARED', 'MAP_PRIVATE',
+    constant_names = ['MAP_SHARED', 'MAP_PRIVATE', 'MAP_FIXED',
                       'PROT_READ', 'PROT_WRITE',
                       'MS_SYNC']
     opt_constant_names = ['MAP_ANON', 'MAP_ANONYMOUS', 'MAP_NORESERVE',
@@ -675,10 +675,17 @@
         return m
 
     def alloc_hinted(hintp, map_size):
-        flags = MAP_PRIVATE | MAP_ANONYMOUS
-        prot = PROT_EXEC | PROT_READ | PROT_WRITE
+        flags = NonConstant(MAP_PRIVATE | MAP_ANONYMOUS)
+        prot = NonConstant(PROT_EXEC | PROT_READ | PROT_WRITE)
         return c_mmap_safe(hintp, map_size, prot, flags, -1, 0)
 
+    def clear_large_memory_chunk_aligned(addr, map_size):
+        addr = rffi.cast(PTR, addr)
+        flags = NonConstant(MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS)
+        prot = NonConstant(PROT_READ | PROT_WRITE)
+        res = c_mmap_safe(addr, map_size, prot, flags, -1, 0)
+        return res == addr
+
     # XXX is this really necessary?
     class Hint:
         pos = -0x4fff0000   # for reproducible results
diff --git a/rpython/rtyper/lltypesystem/llarena.py b/rpython/rtyper/lltypesystem/llarena.py
--- a/rpython/rtyper/lltypesystem/llarena.py
+++ b/rpython/rtyper/lltypesystem/llarena.py
@@ -409,41 +409,28 @@
 
 MEMORY_ALIGNMENT = memory_alignment()
 
-if sys.platform.startswith('linux'):
-    # This only works with linux's madvise(), which is really not a memory
-    # usage hint but a real command.  It guarantees that after MADV_DONTNEED
-    # the pages are cleared again.
+if os.name == 'posix':
+    # The general Posix solution to clear a large range of memory that
+    # was obtained with mmap() is to call mmap() again with MAP_FIXED.
 
-    # Note that the trick of the general 'posix' section below, i.e.
-    # reading /dev/zero, does not seem to have the correct effect of
-    # lazily-allocating pages on all Linux systems.
+    legacy_getpagesize = rffi.llexternal('getpagesize', [], rffi.INT,
+                                         sandboxsafe=True, _nowrapper=True)
 
-    from rpython.rtyper.tool import rffi_platform
-    from rpython.translator.tool.cbuild import ExternalCompilationInfo
-    _eci = ExternalCompilationInfo(includes=['sys/mman.h'])
-    MADV_DONTNEED = rffi_platform.getconstantinteger('MADV_DONTNEED',
-                                                     '#include <sys/mman.h>')
-    linux_madvise = rffi.llexternal('madvise',
-                                    [llmemory.Address, rffi.SIZE_T, rffi.INT],
-                                    rffi.INT,
-                                    sandboxsafe=True, _nowrapper=True,
-                                    compilation_info=_eci)
-    linux_getpagesize = rffi.llexternal('getpagesize', [], rffi.INT,
-                                        sandboxsafe=True, _nowrapper=True,
-                                        compilation_info=_eci)
-
-    class LinuxPageSize:
+    class PosixPageSize:
         def __init__(self):
             self.pagesize = 0
         def _cleanup_(self):
             self.pagesize = 0
-    linuxpagesize = LinuxPageSize()
+    posixpagesize = PosixPageSize()
 
     def clear_large_memory_chunk(baseaddr, size):
-        pagesize = linuxpagesize.pagesize
+        from rpython.rlib import rmmap
+
+        pagesize = posixpagesize.pagesize
         if pagesize == 0:
-            pagesize = rffi.cast(lltype.Signed, linux_getpagesize())
-            linuxpagesize.pagesize = pagesize
+            pagesize = rffi.cast(lltype.Signed, legacy_getpagesize())
+            posixpagesize.pagesize = pagesize
+
         if size > 2 * pagesize:
             lowbits = rffi.cast(lltype.Signed, baseaddr) & (pagesize - 1)
             if lowbits:     # clear the initial misaligned part, if any
@@ -452,56 +439,13 @@
                 baseaddr += partpage
                 size -= partpage
             length = size & -pagesize
-            madv_length = rffi.cast(rffi.SIZE_T, length)
-            madv_flags = rffi.cast(rffi.INT, MADV_DONTNEED)
-            err = linux_madvise(baseaddr, madv_length, madv_flags)
-            if rffi.cast(lltype.Signed, err) == 0:
-                baseaddr += length     # madvise() worked
+            if rmmap.clear_large_memory_chunk_aligned(baseaddr, length):
+                baseaddr += length     # clearing worked
                 size -= length
+
         if size > 0:    # clear the final misaligned part, if any
             llmemory.raw_memclear(baseaddr, size)
 
-elif os.name == 'posix':
-    READ_MAX = (sys.maxint//4) + 1    # upper bound on reads to avoid surprises
-    raw_os_open = rffi.llexternal('open',
-                                  [rffi.CCHARP, rffi.INT, rffi.MODE_T],
-                                  rffi.INT,
-                                  sandboxsafe=True, _nowrapper=True)
-    raw_os_read = rffi.llexternal('read',
-                                  [rffi.INT, llmemory.Address, rffi.SIZE_T],
-                                  rffi.SIZE_T,
-                                  sandboxsafe=True, _nowrapper=True)
-    raw_os_close = rffi.llexternal('close',
-                                   [rffi.INT],
-                                   rffi.INT,
-                                   sandboxsafe=True, _nowrapper=True)
-    _dev_zero = rffi.str2charp('/dev/zero')   # prebuilt
-    lltype.render_immortal(_dev_zero)
-
-    def clear_large_memory_chunk(baseaddr, size):
-        # on some Unixy platforms, reading from /dev/zero is the fastest way
-        # to clear arenas, because the kernel knows that it doesn't
-        # need to even allocate the pages before they are used.
-
-        # NB.: careful, don't do anything that could malloc here!
-        # this code is called during GC initialization.
-        fd = raw_os_open(_dev_zero,
-                         rffi.cast(rffi.INT, os.O_RDONLY),
-                         rffi.cast(rffi.MODE_T, 0644))
-        if rffi.cast(lltype.Signed, fd) != -1:
-            while size > 0:
-                size1 = rffi.cast(rffi.SIZE_T, min(READ_MAX, size))
-                count = raw_os_read(fd, baseaddr, size1)
-                count = rffi.cast(lltype.Signed, count)
-                if count <= 0:
-                    break
-                size -= count
-                baseaddr += count
-            raw_os_close(fd)
-
-        if size > 0:     # reading from /dev/zero failed, fallback
-            llmemory.raw_memclear(baseaddr, size)
-
 else:
     # XXX any better implementation on Windows?
     # Should use VirtualAlloc() to reserve the range of pages,


More information about the pypy-commit mailing list