1 HANDLE
fileDesHandle (int fd
);
5 static HANDLE
tempFileDes (void) {
6 /* Based on http://msdn.microsoft.com/en-us/library/aa363875(VS.85).aspx */
9 DWORD dwBufSize
=BUFSIZE
;
11 TCHAR szTempName
[BUFSIZE
];
12 TCHAR lpPathBuffer
[BUFSIZE
];
14 dwRetVal
= GetTempPath(dwBufSize
, lpPathBuffer
);
15 if (dwRetVal
> dwBufSize
|| (dwRetVal
== 0))
16 die ("GetTempPath(%ld,...) failed with error %ld\n",
17 dwBufSize
, GetLastError());
18 uRetVal
= GetTempFileName(lpPathBuffer
, TEXT("MLtonTempFile"), 0, szTempName
);
20 die ("GetTempFileName(\"%s\",...) failed with error %ld\n",
21 lpPathBuffer
, GetLastError());
22 hTempFile
= CreateFile((LPTSTR
) szTempName
,
23 GENERIC_READ
| GENERIC_WRITE
,
27 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_DELETE_ON_CLOSE
,
29 if (hTempFile
== INVALID_HANDLE_VALUE
)
30 die ("CreateFile(\"%s\",...) failed with error %ld\n",
31 szTempName
, GetLastError());
39 void GC_diskBack_read (void *data
, pointer buf
, size_t size
) {
44 h
= ((WriteToDiskData
)data
)->handle
;
45 d
= SetFilePointer (h
, 0, NULL
, FILE_BEGIN
);
46 if (d
== INVALID_SET_FILE_POINTER
)
47 die ("SetFilePointer failed with error %ld\n", GetLastError());
48 unless (ReadFile(h
, buf
, size
, &dwBytesRead
, NULL
))
49 die ("ReadFile failed with error %ld\n", GetLastError());
52 void GC_diskBack_close (void *data
) {
55 h
= ((WriteToDiskData
)data
)->handle
;
56 unless (CloseHandle (h
))
57 die ("CloseHandle failed with error %ld.", GetLastError());
61 void *GC_diskBack_write (pointer buf
, size_t size
) {
67 unless (WriteFile (h
, buf
, size
, &dwBytesWritten
, NULL
))
68 die ("WriteFile failed with error %ld\n", GetLastError());
69 d
= (WriteToDiskData
)(malloc_safe (sizeof(*d
)));
74 static void displayMaps (void) {
75 MEMORY_BASIC_INFORMATION buf
;
76 const char *state
= "<unset>";
77 const char *protect
= "<unset>";
82 address
+ buf
.RegionSize
>= address
;
83 address
+= buf
.RegionSize
) {
84 if (0 == VirtualQuery ((LPCVOID
)address
, &buf
, sizeof (buf
)))
86 if (0 == buf
.RegionSize
)
89 switch (buf
.Protect
) {
91 protect
= "PAGE_READONLY";
94 protect
= "PAGE_READWRITE";
97 protect
= "PAGE_WRITECOPY";
100 protect
= "PAGE_EXECUTE";
102 case PAGE_EXECUTE_READ
:
103 protect
= "PAGE_EXECUTE_READ";
105 case PAGE_EXECUTE_READWRITE
:
106 protect
= "PAGE_EXECUTE_READWRITE";
108 case PAGE_EXECUTE_WRITECOPY
:
109 protect
= "PAGE_EXECUTE_WRITECOPY";
112 protect
= "PAGE_GUARD";
115 protect
= "PAGE_NOACCESS";
118 protect
= "PAGE_NOCACHE";
125 state
= "MEM_COMMIT";
131 state
= "MEM_RESERVE";
137 fprintf(stderr
, FMTPTR
" %10"PRIuMAX
" %s %s\n",
138 (uintptr_t)buf
.BaseAddress
, (uintmax_t)buf
.RegionSize
,
143 void GC_displayMem (void) {
146 ms
.dwLength
= sizeof (MEMORYSTATUSEX
);
147 GlobalMemoryStatusEx (&ms
);
149 fprintf(stderr
, "Total Phys. Mem: %"PRIuMAX
"\n"
150 "Avail Phys. Mem: %"PRIuMAX
"\n"
151 "Total Page File: %"PRIuMAX
"\n"
152 "Avail Page File: %"PRIuMAX
"\n"
153 "Total Virtual: %"PRIuMAX
"\n"
154 "Avail Virtual: %"PRIuMAX
"\n",
155 (uintmax_t)ms
.ullTotalPhys
,
156 (uintmax_t)ms
.ullAvailPhys
,
157 (uintmax_t)ms
.ullTotalPageFile
,
158 (uintmax_t)ms
.ullAvailPageFile
,
159 (uintmax_t)ms
.ullTotalVirtual
,
160 (uintmax_t)ms
.ullAvailVirtual
);
163 ms
.dwLength
= sizeof (MEMORYSTATUS
);
164 GlobalMemoryStatus (&ms
);
166 fprintf(stderr
, "Total Phys. Mem: %"PRIuMAX
"\n"
167 "Avail Phys. Mem: %"PRIuMAX
"\n"
168 "Total Page File: %"PRIuMAX
"\n"
169 "Avail Page File: %"PRIuMAX
"\n"
170 "Total Virtual: %"PRIuMAX
"\n"
171 "Avail Virtual: %"PRIuMAX
"\n",
172 (uintmax_t)ms
.dwTotalPhys
,
173 (uintmax_t)ms
.dwAvailPhys
,
174 (uintmax_t)ms
.dwTotalPageFile
,
175 (uintmax_t)ms
.dwAvailPageFile
,
176 (uintmax_t)ms
.dwTotalVirtual
,
177 (uintmax_t)ms
.dwAvailVirtual
);
182 static HANDLE
dupHandle (int fd
) {
186 raw
= fileDesHandle (fd
);
187 if (raw
== (HANDLE
)-1 or raw
== 0) {
191 /* 'Inspired' by http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/creating_a_child_process_with_redirected_input_and_output.asp
192 * It's interesting that you can open files for/from other processes...
194 unless (DuplicateHandle (
195 GetCurrentProcess(), /* source process */
196 raw
, /* source handle */
197 GetCurrentProcess(), /* target process */
198 &dupd
, /* target handle - valid in target proc */
199 0, /* ignored b/c DUPLICATE_SAME_ACCESS used */
200 TRUE
, /* this can be inherited by children */
201 DUPLICATE_SAME_ACCESS
))/* keep the same permissions */
209 /* Windows memory is allocated in two phases: reserve and commit.
210 * A reservation makes the address space unavailable to other reservations.
211 * Commiting reserved memory actually maps the reserved memory for use.
212 * Decommitting a portion of a reservation releases the physical memory only.
213 * The complicating detail is that one cannot partially release a reservation.
215 * The management routines below manage a 'heap' that is composed of several
216 * distinct reservations, laid out in the following order:
217 * 0+ reservations set MEM_COMMIT
218 * 1 reservation starting MEM_COMMIT with an optional MEM_RESERVE tail
220 * The heap always starts on a reservation and ends at where the MEM_RESERVE
221 * region (if any) begins.
224 /* Create a new heap */
225 static inline void *Windows_mmapAnon (void *base
, size_t length
) {
228 /* We prevoiusly used "0" instead of start, which lead to crashes.
229 * After reading win32 documentation, the reason for these crashes
230 * becomes clear: we were using only MEM_COMMIT! If there was memory
231 * decommitted in a previous heap shrink, a new heap might end up
232 * inside the reserved (but uncommitted) memory. When the old heap is
233 * freed, it will kill the new heap as well. This bug will not happen
234 * now because we reserve, then commit. Reserved memory cannot conflict.
236 res
= VirtualAlloc (base
, length
, MEM_RESERVE
, PAGE_NOACCESS
);
240 /* Actually get the memory for use */
241 if (0 == VirtualAlloc (res
, length
, MEM_COMMIT
, PAGE_READWRITE
)) {
242 VirtualFree(res
, 0, MEM_RELEASE
);
249 static inline void Windows_release (void *base
, size_t length
) {
250 MEMORY_BASIC_INFORMATION mi
;
252 if (length
== 0) return;
254 /* We might not be able to release the first reservation because
255 * it overlaps the base address we wish to keep. The idea is to
256 * decommit the part we don't need, and release all reservations
257 * that may be after this point.
260 if (0 == VirtualQuery(base
, &mi
, sizeof(mi
)))
261 die("VirtualQuery failed");
262 assert (mi
.State
!= MEM_FREE
);
263 assert (mi
.RegionSize
<= length
);
265 if (mi
.AllocationBase
!= base
) {
266 if (0 == VirtualFree(base
, mi
.RegionSize
, MEM_DECOMMIT
))
267 die("VirtualFree(MEM_DECOMMIT)");
269 /* Requery: the following region might also be decommit */
270 VirtualQuery(base
, &mi
, sizeof(mi
));
271 assert (mi
.State
== MEM_RESERVE
);
273 /* It's possible the consolidated reserved space is larger
274 * than the range we were asked to free. Bail out early.
276 if (mi
.RegionSize
>= length
) return;
278 /* Skip decommited region and move to the next reservation */
279 base
= (char*)base
+ mi
.RegionSize
;
280 length
-= mi
.RegionSize
;
283 /* Clean-up the remaining tail. */
285 if (0 == VirtualQuery(base
, &mi
, sizeof(mi
)))
288 /* We should never have a completely decommitted alloc */
289 assert (mi
.State
== MEM_COMMIT
);
290 /* This method is supposed to only do complete releases */
291 assert (mi
.AllocationBase
== base
);
292 /* The committed region should never exceed the length */
293 assert (mi
.RegionSize
<= length
);
295 if (0 == VirtualFree(base
, 0, MEM_RELEASE
))
296 die("VirtualFree(MEM_RELEASE) failed");
298 base
= (char*)base
+ mi
.RegionSize
;
299 length
-= mi
.RegionSize
;
302 /* The last release also handled the optional MEM_RESERVE region */
305 /* Extend an existing heap */
306 static inline void* Windows_extend (void *base
, size_t length
) {
307 MEMORY_BASIC_INFORMATION mi
;
310 /* Check the status of memory after the end of the allocation */
311 VirtualQuery(base
, &mi
, sizeof(mi
));
313 if (mi
.State
== MEM_FREE
) {
314 /* No tail of reserved memory -> simply try to allocate */
315 return Windows_mmapAnon(base
, length
);
316 } else if (mi
.State
== MEM_RESERVE
) {
317 assert (mi
.AllocationBase
<= base
);
318 end
= (char*)base
+ mi
.RegionSize
;
320 if (mi
.RegionSize
> length
) { /* only commit is needed */
321 if (0 == VirtualAlloc(base
, length
,
322 MEM_COMMIT
, PAGE_READWRITE
)) {
327 } else if (end
== Windows_mmapAnon(end
, length
-mi
.RegionSize
)) {
328 if (0 == VirtualAlloc(base
, mi
.RegionSize
,
329 MEM_COMMIT
, PAGE_READWRITE
)) {
330 VirtualFree(end
, 0, MEM_RELEASE
);
336 /* Failed to allocate tail */
340 /* The memory is used by another mapping */
346 Windows_Process_create (NullString8_t cmds
, NullString8_t args
, NullString8_t envs
,
347 C_Fd_t in
, C_Fd_t out
, C_Fd_t err
) {
352 PROCESS_INFORMATION pi
;
358 ZeroMemory (&si
, sizeof(STARTUPINFO
));
359 si
.cb
= sizeof(STARTUPINFO
);
360 si
.hStdInput
= dupHandle (in
);
361 si
.hStdOutput
= dupHandle (out
);
362 si
.hStdError
= dupHandle (err
);
363 si
.dwFlags
= STARTF_USESTDHANDLES
; /* use the above */
364 if (!si
.hStdInput
or !si
.hStdOutput
or !si
.hStdError
) {
365 if (si
.hStdInput
) CloseHandle (si
.hStdInput
);
366 if (si
.hStdOutput
) CloseHandle (si
.hStdOutput
);
367 if (si
.hStdError
) CloseHandle (si
.hStdError
);
368 /* errno already faked by dupHandle */
371 ZeroMemory (&pi
, sizeof(PROCESS_INFORMATION
));
372 unless (CreateProcess (
373 cmd
, /* Module name */
374 arg
, /* Command line */
375 NULL
, /* Process handle not inheritable */
376 NULL
, /* Thread handle not inheritable */
377 TRUE
, /* Set handle inheritance to TRUE */
378 0, /* No creation flags */
379 env
, /* Environment */
380 NULL
, /* Use parent's starting directory */
381 &si
, /* Pointer to STARTUPINFO structure */
382 &pi
/* Pointer to PROCESS_INFORMATION structure */
384 errno
= ENOENT
; /* probably does not exist (aka ENOFILE)*/
387 /* Process created successfully */
388 /* We will return the process handle for the 'pid'.
389 * This way we can TerminateProcess (kill) it and
390 * WaitForSingleObject/GetExitCodeProcess (reap) it.
391 * The thread handle is not needed, so clean it.
393 CloseHandle (pi
.hThread
);
394 CloseHandle (si
.hStdInput
);
395 CloseHandle (si
.hStdOutput
);
396 CloseHandle (si
.hStdError
);
398 return (C_PId_t
)pi
.hProcess
;
402 Windows_Process_createNull (NullString8_t cmds
, NullString8_t args
,
403 C_Fd_t in
, C_Fd_t out
, C_Fd_t err
) {
404 return Windows_Process_create (cmds
, args
, NULL
, in
, out
, err
);
407 C_Errno_t(C_Int_t
) Windows_Process_getexitcode (C_PId_t pid
, Ref(C_Status_t
) status
) {
411 unless (WaitForSingleObject (h
, INFINITE
) == WAIT_OBJECT_0
) {
415 unless (GetExitCodeProcess (h
, (DWORD
*)status
)) {
422 C_Errno_t(C_Int_t
) Windows_Process_terminate (C_PId_t pid
, C_Signal_t sig
) {
426 unless (TerminateProcess (h
, 0x80000000UL
| sig
)) {