1 /**
2 * Contains OS-level routines needed by the garbage collector.
3 *
4 * Copyright: D Language Foundation 2005 - 2021.
5 * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
6 * Authors: Walter Bright, David Friedman, Sean Kelly, Leandro Lucarella
7 */
8 module core.internal.gc.os;
9
10
version(Windows)11 version (Windows)
12 {
13 import core.sys.windows.winbase : GetCurrentThreadId, VirtualAlloc, VirtualFree;
14 import core.sys.windows.winnt : MEM_COMMIT, MEM_RELEASE, MEM_RESERVE, PAGE_READWRITE;
15
16 alias int pthread_t;
17
18 pthread_t pthread_self() nothrow
19 {
20 return cast(pthread_t) GetCurrentThreadId();
21 }
22
23 //version = GC_Use_Alloc_Win32;
24 }
version(Posix)25 else version (Posix)
26 {
27 version (OSX)
28 version = Darwin;
29 else version (iOS)
30 version = Darwin;
31 else version (TVOS)
32 version = Darwin;
33 else version (WatchOS)
34 version = Darwin;
35
36 import core.sys.posix.sys.mman;
37 import core.stdc.stdlib;
38
39
40 /// Possible results for the wait_pid() function.
41 enum ChildStatus
42 {
43 done, /// The process has finished successfully
44 running, /// The process is still running
45 error /// There was an error waiting for the process
46 }
47
48 /**
49 * Wait for a process with PID pid to finish.
50 *
51 * If block is false, this function will not block, and return ChildStatus.running if
52 * the process is still running. Otherwise it will return always ChildStatus.done
53 * (unless there is an error, in which case ChildStatus.error is returned).
54 */
55 ChildStatus wait_pid(pid_t pid, bool block = true) nothrow @nogc
56 {
57 import core.exception : onForkError;
58
59 int status = void;
60 pid_t waited_pid = void;
61 // In the case where we are blocking, we need to consider signals
62 // arriving while we wait, and resume the waiting if EINTR is returned
63 do {
64 errno = 0;
65 waited_pid = waitpid(pid, &status, block ? 0 : WNOHANG);
66 }
67 while (waited_pid == -1 && errno == EINTR);
68 if (waited_pid == 0)
69 return ChildStatus.running;
70 else if (errno == ECHILD)
71 return ChildStatus.done; // someone called posix.syswait
72 else if (waited_pid != pid || status != 0)
73 {
74 onForkError();
75 return ChildStatus.error;
76 }
77 return ChildStatus.done;
78 }
79
80 public import core.sys.posix.unistd: pid_t, fork;
81 import core.sys.posix.sys.wait: waitpid, WNOHANG;
82 import core.stdc.errno: errno, EINTR, ECHILD;
83
84 //version = GC_Use_Alloc_MMap;
85 }
86 else
87 {
88 import core.stdc.stdlib;
89
90 //version = GC_Use_Alloc_Malloc;
91 }
92
93 /+
94 static if (is(typeof(VirtualAlloc)))
95 version = GC_Use_Alloc_Win32;
96 else static if (is(typeof(mmap)))
97 version = GC_Use_Alloc_MMap;
98 else static if (is(typeof(valloc)))
99 version = GC_Use_Alloc_Valloc;
100 else static if (is(typeof(malloc)))
101 version = GC_Use_Alloc_Malloc;
102 else static assert(false, "No supported allocation methods available.");
103 +/
104
105 static if (is(typeof(VirtualAlloc))) // version (GC_Use_Alloc_Win32)
106 {
107 /**
108 * Indicates if an implementation supports fork().
109 *
110 * The value shown here is just demostrative, the real value is defined based
111 * on the OS it's being compiled in.
112 * enum HaveFork = true;
113 */
114 enum HaveFork = false;
115
116 /**
117 * Map memory.
118 */
os_mem_map(size_t nbytes)119 void *os_mem_map(size_t nbytes) nothrow @nogc
120 {
121 return VirtualAlloc(null, nbytes, MEM_RESERVE | MEM_COMMIT,
122 PAGE_READWRITE);
123 }
124
125
126 /**
127 * Unmap memory allocated with os_mem_map().
128 * Returns:
129 * 0 success
130 * !=0 failure
131 */
os_mem_unmap(void * base,size_t nbytes)132 int os_mem_unmap(void *base, size_t nbytes) nothrow @nogc
133 {
134 return cast(int)(VirtualFree(base, 0, MEM_RELEASE) == 0);
135 }
136 }
137 else static if (is(typeof(mmap))) // else version (GC_Use_Alloc_MMap)
138 {
139 enum HaveFork = true;
140
141 void *os_mem_map(size_t nbytes, bool share = false) nothrow @nogc
142 { void *p;
143
144 auto map_f = share ? MAP_SHARED : MAP_PRIVATE;
145 p = mmap(null, nbytes, PROT_READ | PROT_WRITE, map_f | MAP_ANON, -1, 0);
146 return (p == MAP_FAILED) ? null : p;
147 }
148
149
os_mem_unmap(void * base,size_t nbytes)150 int os_mem_unmap(void *base, size_t nbytes) nothrow @nogc
151 {
152 return munmap(base, nbytes);
153 }
154 }
155 else static if (is(typeof(valloc))) // else version (GC_Use_Alloc_Valloc)
156 {
157 enum HaveFork = false;
158
os_mem_map(size_t nbytes)159 void *os_mem_map(size_t nbytes) nothrow @nogc
160 {
161 return valloc(nbytes);
162 }
163
164
os_mem_unmap(void * base,size_t nbytes)165 int os_mem_unmap(void *base, size_t nbytes) nothrow @nogc
166 {
167 free(base);
168 return 0;
169 }
170 }
171 else static if (is(typeof(malloc))) // else version (GC_Use_Alloc_Malloc)
172 {
173 // NOTE: This assumes malloc granularity is at least (void*).sizeof. If
174 // (req_size + PAGESIZE) is allocated, and the pointer is rounded up
175 // to PAGESIZE alignment, there will be space for a void* at the end
176 // after PAGESIZE bytes used by the GC.
177
178 enum HaveFork = false;
179
180 import core.internal.gc.impl.conservative.gc;
181
182
183 const size_t PAGE_MASK = PAGESIZE - 1;
184
185
os_mem_map(size_t nbytes)186 void *os_mem_map(size_t nbytes) nothrow @nogc
187 { byte *p, q;
188 p = cast(byte *) malloc(nbytes + PAGESIZE);
189 if (!p)
190 return null;
191 q = p + ((PAGESIZE - ((cast(size_t) p & PAGE_MASK))) & PAGE_MASK);
192 * cast(void**)(q + nbytes) = p;
193 return q;
194 }
195
196
os_mem_unmap(void * base,size_t nbytes)197 int os_mem_unmap(void *base, size_t nbytes) nothrow @nogc
198 {
199 free( *cast(void**)( cast(byte*) base + nbytes ) );
200 return 0;
201 }
202 }
203 else
204 {
205 static assert(false, "No supported allocation methods available.");
206 }
207
208 /**
209 Check for any kind of memory pressure.
210
211 Params:
212 mapped = the amount of memory mapped by the GC in bytes
213 Returns:
214 true if memory is scarce
215 */
216 // TODO: get virtual mem sizes and current usage from OS
217 // TODO: compare current RSS and avail. physical memory
isLowOnMem(size_t mapped)218 bool isLowOnMem(size_t mapped) nothrow @nogc
219 {
220 version (Windows)
221 {
222 import core.sys.windows.winbase : GlobalMemoryStatusEx, MEMORYSTATUSEX;
223
224 MEMORYSTATUSEX stat;
225 stat.dwLength = stat.sizeof;
226 const success = GlobalMemoryStatusEx(&stat) != 0;
227 assert(success, "GlobalMemoryStatusEx() failed");
228 if (!success)
229 return false;
230
231 // dwMemoryLoad is the 'approximate percentage of physical memory that is in use'
232 // https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/ns-sysinfoapi-memorystatusex
233 const percentPhysicalRAM = stat.ullTotalPhys / 100;
234 return (stat.dwMemoryLoad >= 95 && mapped > percentPhysicalRAM)
235 || (stat.dwMemoryLoad >= 90 && mapped > 10 * percentPhysicalRAM);
236 }
237 else
238 {
239 enum GB = 2 ^^ 30;
240 version (D_LP64)
241 return false;
242 else version (Darwin)
243 {
244 // 80 % of available 4GB is used for GC (excluding malloc and mmap)
245 enum size_t limit = 4UL * GB * 8 / 10;
246 return mapped > limit;
247 }
248 else
249 {
250 // be conservative and assume 3GB
251 enum size_t limit = 3UL * GB * 8 / 10;
252 return mapped > limit;
253 }
254 }
255 }
256
257 /**
258 Get the size of available physical memory
259
260 Returns:
261 size of installed physical RAM
262 */
version(Windows)263 version (Windows)
264 {
265 ulong os_physical_mem() nothrow @nogc
266 {
267 import core.sys.windows.winbase : GlobalMemoryStatus, MEMORYSTATUS;
268 MEMORYSTATUS stat;
269 GlobalMemoryStatus(&stat);
270 return stat.dwTotalPhys; // limited to 4GB for Win32
271 }
272 }
version(Darwin)273 else version (Darwin)
274 {
275 extern (C) int sysctl(const int* name, uint namelen, void* oldp, size_t* oldlenp, const void* newp, size_t newlen) @nogc nothrow;
276 ulong os_physical_mem() nothrow @nogc
277 {
278 enum
279 {
280 CTL_HW = 6,
281 HW_MEMSIZE = 24,
282 }
283 int[2] mib = [ CTL_HW, HW_MEMSIZE ];
284 ulong system_memory_bytes;
285 size_t len = system_memory_bytes.sizeof;
286 if (sysctl(mib.ptr, 2, &system_memory_bytes, &len, null, 0) != 0)
287 return 0;
288 return system_memory_bytes;
289 }
290 }
version(Posix)291 else version (Posix)
292 {
293 ulong os_physical_mem() nothrow @nogc
294 {
295 import core.sys.posix.unistd : sysconf, _SC_PAGESIZE, _SC_PHYS_PAGES;
296 const pageSize = sysconf(_SC_PAGESIZE);
297 const pages = sysconf(_SC_PHYS_PAGES);
298 return pageSize * pages;
299 }
300 }
301