1 /**
2 Copyright: Copyright (c) 2017-2019 Andrey Penechko.
3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 Authors: Andrey Penechko.
5 */
6 module vox.utils.arenapool;
7 
8 version(Posix) extern (C) int getpagesize();
9 
10 // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/mmap.2.html
11 // https://stackoverflow.com/questions/21809072/virtual-memory-on-osx-ios-versus-windows-commit-reserve-behaviour
12 
13 ///
14 struct ArenaPool
15 {
16 	import vox.utils : alignValue;
17 	import std.format;
18 	import std.stdio;
19 	import std.string : fromStringz;
20 	import core.stdc.errno : errno;
21 	import core.stdc.string : strerror;
22 
23 	enum PAGE_SIZE = 65_536;
24 	ubyte[] buffer;
25 	size_t takenBytes;
26 
27 	void reserve(size_t size) {
28 		size_t reservedBytes = alignValue(size, PAGE_SIZE); // round up to page size
29 		version(Posix) {
30 			import core.sys.posix.sys.mman : mmap, MAP_ANON, PROT_READ, PROT_WRITE, PROT_EXEC, MAP_PRIVATE, MAP_FAILED;
31 			enum MAP_NORESERVE = 0x4000;
32 			auto flags = MAP_PRIVATE | MAP_ANON;
33 			// MacOS doesn't support MAP_NORESERVE. See https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/mmap.2.html
34 			version(linux) flags |= MAP_NORESERVE;
35 			ubyte* ptr = cast(ubyte*)mmap(null, reservedBytes, PROT_READ | PROT_WRITE, flags, -1, 0);
36 			assert(ptr != MAP_FAILED, format("mmap failed, errno %s, %s: requested %s bytes", errno, strerror(errno).fromStringz, size));
37 		} else version(Windows) {
38 			import vox.utils.windows : VirtualAlloc, MEM_RESERVE, PAGE_NOACCESS;
39 			ubyte* ptr = cast(ubyte*)VirtualAlloc(null, reservedBytes, MEM_RESERVE, PAGE_NOACCESS);
40 			assert(ptr !is null, format("VirtualAlloc failed: requested %s bytes", size));
41 		}
42 		buffer = ptr[0..reservedBytes];
43 	}
44 
45 	ubyte[] take(size_t numBytes) {
46 		if (numBytes == 0) return null;
47 		ubyte[] result = buffer[takenBytes..takenBytes+numBytes];
48 		takenBytes += numBytes;
49 		return result;
50 	}
51 
52 	void decommitAll() {
53 		version(Posix) {
54 			import core.sys.posix.sys.mman : munmap;
55 			if (buffer.ptr is null) return;
56 			int res = munmap(buffer.ptr, buffer.length);
57 			assert(res == 0, format("munmap(%X, %s) failed, %s, %s", buffer.ptr, buffer.length, errno, strerror(errno).fromStringz));
58 		} else version(Windows) {
59 			import vox.utils.windows : VirtualFree, MEM_DECOMMIT;
60 			int res = VirtualFree(buffer.ptr, buffer.length, MEM_DECOMMIT);
61 			assert(res != 0, "VirtualFree failed");
62 		}
63 	}
64 }