| /** | |
| * Contains OS-level routines needed by the garbage collector. | |
| * | |
| * Copyright: Copyright Digital Mars 2005 - 2013. | |
| * License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). | |
| * Authors: Walter Bright, David Friedman, Sean Kelly, Leandro Lucarella | |
| */ | |
| /* Copyright Digital Mars 2005 - 2013. | |
| * Distributed under the Boost Software License, Version 1.0. | |
| * (See accompanying file LICENSE or copy at | |
| * http://www.boost.org/LICENSE_1_0.txt) | |
| */ | |
| module gc.os; | |
| version (Windows) | |
| { | |
| import core.sys.windows.winbase : GetCurrentThreadId, VirtualAlloc, VirtualFree; | |
| import core.sys.windows.winnt : MEM_COMMIT, MEM_RELEASE, MEM_RESERVE, PAGE_READWRITE; | |
| alias int pthread_t; | |
| pthread_t pthread_self() nothrow | |
| { | |
| return cast(pthread_t) GetCurrentThreadId(); | |
| } | |
| //version = GC_Use_Alloc_Win32; | |
| } | |
| else version (Posix) | |
| { | |
| version (OSX) | |
| version = Darwin; | |
| else version (iOS) | |
| version = Darwin; | |
| else version (TVOS) | |
| version = Darwin; | |
| else version (WatchOS) | |
| version = Darwin; | |
| import core.sys.posix.sys.mman; | |
| version (FreeBSD) import core.sys.freebsd.sys.mman : MAP_ANON; | |
| version (DragonFlyBSD) import core.sys.dragonflybsd.sys.mman : MAP_ANON; | |
| version (NetBSD) import core.sys.netbsd.sys.mman : MAP_ANON; | |
| version (OpenBSD) import core.sys.openbsd.sys.mman : MAP_ANON; | |
| version (CRuntime_Glibc) import core.sys.linux.sys.mman : MAP_ANON; | |
| version (Darwin) import core.sys.darwin.sys.mman : MAP_ANON; | |
| version (CRuntime_UClibc) import core.sys.linux.sys.mman : MAP_ANON; | |
| import core.stdc.stdlib; | |
| //version = GC_Use_Alloc_MMap; | |
| } | |
| else | |
| { | |
| import core.stdc.stdlib; | |
| //version = GC_Use_Alloc_Malloc; | |
| } | |
| /+ | |
| static if (is(typeof(VirtualAlloc))) | |
| version = GC_Use_Alloc_Win32; | |
| else static if (is(typeof(mmap))) | |
| version = GC_Use_Alloc_MMap; | |
| else static if (is(typeof(valloc))) | |
| version = GC_Use_Alloc_Valloc; | |
| else static if (is(typeof(malloc))) | |
| version = GC_Use_Alloc_Malloc; | |
| else static assert(false, "No supported allocation methods available."); | |
| +/ | |
| static if (is(typeof(VirtualAlloc))) // version (GC_Use_Alloc_Win32) | |
| { | |
| /** | |
| * Map memory. | |
| */ | |
| void *os_mem_map(size_t nbytes) nothrow | |
| { | |
| return VirtualAlloc(null, nbytes, MEM_RESERVE | MEM_COMMIT, | |
| PAGE_READWRITE); | |
| } | |
| /** | |
| * Unmap memory allocated with os_mem_map(). | |
| * Returns: | |
| * 0 success | |
| * !=0 failure | |
| */ | |
| int os_mem_unmap(void *base, size_t nbytes) nothrow | |
| { | |
| return cast(int)(VirtualFree(base, 0, MEM_RELEASE) == 0); | |
| } | |
| } | |
| else static if (is(typeof(mmap))) // else version (GC_Use_Alloc_MMap) | |
| { | |
| void *os_mem_map(size_t nbytes) nothrow | |
| { void *p; | |
| p = mmap(null, nbytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); | |
| return (p == MAP_FAILED) ? null : p; | |
| } | |
| int os_mem_unmap(void *base, size_t nbytes) nothrow | |
| { | |
| return munmap(base, nbytes); | |
| } | |
| } | |
| else static if (is(typeof(valloc))) // else version (GC_Use_Alloc_Valloc) | |
| { | |
| void *os_mem_map(size_t nbytes) nothrow | |
| { | |
| return valloc(nbytes); | |
| } | |
| int os_mem_unmap(void *base, size_t nbytes) nothrow | |
| { | |
| free(base); | |
| return 0; | |
| } | |
| } | |
| else static if (is(typeof(malloc))) // else version (GC_Use_Alloc_Malloc) | |
| { | |
| // NOTE: This assumes malloc granularity is at least (void*).sizeof. If | |
| // (req_size + PAGESIZE) is allocated, and the pointer is rounded up | |
| // to PAGESIZE alignment, there will be space for a void* at the end | |
| // after PAGESIZE bytes used by the GC. | |
| import gc.gc; | |
| const size_t PAGE_MASK = PAGESIZE - 1; | |
| void *os_mem_map(size_t nbytes) nothrow | |
| { byte *p, q; | |
| p = cast(byte *) malloc(nbytes + PAGESIZE); | |
| q = p + ((PAGESIZE - ((cast(size_t) p & PAGE_MASK))) & PAGE_MASK); | |
| * cast(void**)(q + nbytes) = p; | |
| return q; | |
| } | |
| int os_mem_unmap(void *base, size_t nbytes) nothrow | |
| { | |
| free( *cast(void**)( cast(byte*) base + nbytes ) ); | |
| return 0; | |
| } | |
| } | |
| else | |
| { | |
| static assert(false, "No supported allocation methods available."); | |
| } | |
| /** | |
| Check for any kind of memory pressure. | |
| Params: | |
| mapped = the amount of memory mapped by the GC in bytes | |
| Returns: | |
| true if memory is scarce | |
| */ | |
| // TOOD: get virtual mem sizes and current usage from OS | |
| // TODO: compare current RSS and avail. physical memory | |
| version (Windows) | |
| { | |
| bool isLowOnMem(size_t mapped) nothrow @nogc | |
| { | |
| version (D_LP64) | |
| return false; | |
| else | |
| { | |
| import core.sys.windows.winbase : GlobalMemoryStatus, MEMORYSTATUS; | |
| MEMORYSTATUS stat; | |
| GlobalMemoryStatus(&stat); | |
| // Less than 5 % of virtual address space available | |
| return stat.dwAvailVirtual < stat.dwTotalVirtual / 20; | |
| } | |
| } | |
| } | |
| else version (Darwin) | |
| { | |
| bool isLowOnMem(size_t mapped) nothrow @nogc | |
| { | |
| enum GB = 2 ^^ 30; | |
| version (D_LP64) | |
| return false; | |
| else | |
| { | |
| // 80 % of available 4GB is used for GC (excluding malloc and mmap) | |
| enum size_t limit = 4UL * GB * 8 / 10; | |
| return mapped > limit; | |
| } | |
| } | |
| } | |
| else | |
| { | |
| bool isLowOnMem(size_t mapped) nothrow @nogc | |
| { | |
| enum GB = 2 ^^ 30; | |
| version (D_LP64) | |
| return false; | |
| else | |
| { | |
| // be conservative and assume 3GB | |
| enum size_t limit = 3UL * GB * 8 / 10; | |
| return mapped > limit; | |
| } | |
| } | |
| } |