1 // Written in the D programming language. 2 /** 3 Source: $(PHOBOSSRC std/experimental/allocator/_mmap_allocator.d) 4 */ 5 module std.experimental.allocator.mmap_allocator; 6 7 /** 8 Allocator (currently defined only for Posix and Windows) using 9 $(D $(LINK2 https://en.wikipedia.org/wiki/Mmap, mmap)) 10 and $(D $(LUCKY munmap)) directly (or their Windows equivalents). There is no 11 additional structure: each call to `allocate(s)` issues a call to 12 $(D mmap(null, s, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)), 13 and each call to `deallocate(b)` issues $(D munmap(b.ptr, b.length)). 14 So `MmapAllocator` is usually intended for allocating large chunks to be 15 managed by fine-granular allocators. 16 */ 17 struct MmapAllocator 18 { 19 /// The one shared instance. 20 static shared const MmapAllocator instance; 21 22 /** 23 Alignment is page-size and hardcoded to 4096 (even though on certain systems 24 it could be larger). 25 */ 26 enum size_t alignment = 4096; 27 28 version (Posix) 29 { 30 /// Allocator API. 31 pure nothrow @nogc @safe 32 void[] allocate(size_t bytes) shared const 33 { 34 import core.sys.posix.sys.mman : MAP_ANON, PROT_READ, 35 PROT_WRITE, MAP_PRIVATE, MAP_FAILED; 36 if (!bytes) return null; 37 const errnosave = (() @trusted => fakePureErrno())(); // For purity revert changes to errno. 38 auto p = (() @trusted => fakePureMmap(null, bytes, PROT_READ | PROT_WRITE, 39 MAP_PRIVATE | MAP_ANON, -1, 0))(); 40 if (p is MAP_FAILED) 41 { 42 (() @trusted => fakePureErrno() = errnosave)(); // errno only changed on MAP_FAILED. 43 return null; 44 } 45 return (() @trusted => p[0 .. bytes])(); 46 } 47 48 /// Ditto 49 pure nothrow @nogc 50 bool deallocate(void[] b) shared const 51 { 52 // Because we assert(0) on error we don't need to reset errno for purity. 53 if (b.ptr) fakePureMunmap(b.ptr, b.length) == 0 || assert(0); 54 return true; 55 } 56 57 // Anonymous mmap might be zero-filled on all Posix systems but 58 // not all commit to this in the documentation. 59 version (linux) 60 // http://man7.org/linux/man-pages/man2/mmap.2.html 61 package alias allocateZeroed = allocate; 62 else version (NetBSD) 63 // https://man.netbsd.org/mmap.2 64 package alias allocateZeroed = allocate; 65 else version (Solaris) 66 // https://docs.oracle.com/cd/E88353_01/html/E37841/mmap-2.html 67 package alias allocateZeroed = allocate; 68 else version (AIX) 69 // https://www.ibm.com/support/knowledgecenter/en/ssw_aix_71/com.ibm.aix.basetrf1/mmap.htm 70 package alias allocateZeroed = allocate; 71 } 72 else version (Windows) 73 { 74 import core.sys.windows.winnt : MEM_COMMIT, PAGE_READWRITE, MEM_RELEASE; 75 76 /// Allocator API. 77 pure nothrow @nogc @safe 78 void[] allocate(size_t bytes) shared const 79 { 80 if (!bytes) return null; 81 // For purity ensure last-error does not visibly change. 82 const lastErrorSave = (() @trusted => GetLastError())(); 83 auto p = (() @trusted => VirtualAlloc(null, bytes, MEM_COMMIT, PAGE_READWRITE))(); 84 if (p == null) 85 { 86 // Last-error only changed if allocation failed. 87 (() @trusted => SetLastError(lastErrorSave))(); 88 return null; 89 } 90 return (() @trusted => p[0 .. bytes])(); 91 } 92 93 /// Ditto 94 pure nothrow @nogc 95 bool deallocate(void[] b) shared const 96 { 97 const lastErrorSave = GetLastError(); // For purity ensure last-error does not visibly change. 98 scope(exit) SetLastError(lastErrorSave); 99 return b.ptr is null || VirtualFree(b.ptr, 0, MEM_RELEASE) != 0; 100 } 101 102 package alias allocateZeroed = allocate; 103 } 104 } 105 106 // pure wrappers around `mmap` and `munmap` because they are used here locally 107 // solely to perform allocation and deallocation which in this case is `pure` 108 version (Posix) 109 extern (C) private pure @system @nogc nothrow 110 { 111 import core.sys.posix.sys.types : off_t; 112 pragma(mangle, "fakePureErrnoImpl") ref int fakePureErrno(); 113 pragma(mangle, "mmap") void* fakePureMmap(void*, size_t, int, int, int, off_t); 114 pragma(mangle, "munmap") int fakePureMunmap(void*, size_t); 115 } 116 117 // Pure wrappers around VirtualAlloc/VirtualFree for use here only. Their use is sound 118 // because when we call them we ensure that last-error is not visibly changed. 119 version (Windows) 120 extern (Windows) private pure @system @nogc nothrow 121 { 122 import core.sys.windows.basetsd : SIZE_T; 123 import core.sys.windows.windef : BOOL, DWORD; 124 import core.sys.windows.winnt : LPVOID, PVOID; 125 126 DWORD GetLastError(); 127 void SetLastError(DWORD); 128 PVOID VirtualAlloc(PVOID, SIZE_T, DWORD, DWORD); 129 BOOL VirtualFree(PVOID, SIZE_T, DWORD); 130 } 131 132 pure nothrow @safe @nogc unittest 133 { 134 alias alloc = MmapAllocator.instance; 135 auto p = alloc.allocate(100); 136 assert(p.length == 100); 137 () @trusted { alloc.deallocate(p); p = null; }(); 138 }