feat(launcher): Process.*, Http.save_to/received/expected, App.window_hide/show
Process.spawn/poll/kill/free - non-blocking child processes with no shell: posix_spawn on macOS (process.ll), CreateProcessW with MSVC-quoted arguments and no console window on Windows (process_win.ll), linked only when a program uses Process.*. Http.save_to streams a response body into a file (NSURLSession with a run-time delegate class on macOS, the WinHTTP read loop on Windows); Http.received / Http.expected report progress while it is pending. Freeing a pending request cancels it and parks the slot until the worker has finished. App.window_hide / App.window_show take the game's window off the screen and back without closing it; the run goes on while hidden. Docs, examples, tests and a changeset. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
c1db0d71ed
commit
43d5eb5b88
31 changed files with 89274 additions and 86727 deletions
196
runtime/native/process_win.ll
Normal file
196
runtime/native/process_win.ll
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
; ============================================================================
|
||||
; process_win.ll — child processes behind Process.* (Windows, CreateProcessW).
|
||||
;
|
||||
; process.ll's interface. ludicc links it when a Windows program uses Process.*.
|
||||
;
|
||||
; lu_proc_spawn(path, argv, cmdline) -> int CreateProcessW(path, cmdline): a handle
|
||||
; 0..15, or -1. argv is POSIX's and unused;
|
||||
; cmdline was quoted by process.ludic.
|
||||
; lu_proc_poll(h) -> int -1 while running, else GetExitCodeProcess
|
||||
; lu_proc_kill(h) TerminateProcess, exit code 137
|
||||
; lu_proc_free(h) CloseHandle
|
||||
;
|
||||
; The child inherits the environment and the working directory and no handles, and is
|
||||
; started with CREATE_NO_WINDOW: a GUI parent starting a console program must not flash a
|
||||
; console window up (a GUI child ignores the flag). Paths and the command line cross in
|
||||
; UTF-8 and are converted to UTF-16 here.
|
||||
;
|
||||
; An exit code is a DWORD. One that reads as -1 (0xFFFFFFFF) is reported as 255, so -1
|
||||
; always means "still running"; an NTSTATUS crash code such as 0xC0000005 arrives as a
|
||||
; negative int.
|
||||
; ============================================================================
|
||||
|
||||
declare ptr @malloc(i64)
|
||||
declare void @free(ptr)
|
||||
declare void @llvm.memset.p0.i64(ptr, i8, i64, i1)
|
||||
declare i32 @MultiByteToWideChar(i32, i32, ptr, i32, ptr, i32)
|
||||
declare i32 @CreateProcessW(ptr, ptr, ptr, ptr, i32, i32, ptr, ptr, ptr, ptr)
|
||||
declare i32 @WaitForSingleObject(ptr, i32)
|
||||
declare i32 @GetExitCodeProcess(ptr, ptr)
|
||||
declare i32 @TerminateProcess(ptr, i32)
|
||||
declare i32 @CloseHandle(ptr)
|
||||
|
||||
@P_handle = internal global [16 x ptr] zeroinitializer
|
||||
@P_state = internal global [16 x i32] zeroinitializer ; 0 free, 1 running, 2 ended
|
||||
@P_code = internal global [16 x i32] zeroinitializer
|
||||
|
||||
define internal i1 @pr_bad(i32 %h) {
|
||||
entry:
|
||||
%lo = icmp slt i32 %h, 0
|
||||
%hi = icmp sgt i32 %h, 15
|
||||
%bad = or i1 %lo, %hi
|
||||
ret i1 %bad
|
||||
}
|
||||
|
||||
; a fresh NUL-terminated UTF-16 copy of a UTF-8 string (CP_UTF8 = 65001), or null
|
||||
define internal ptr @pr_wide(ptr %s) {
|
||||
entry:
|
||||
%n = call i32 @MultiByteToWideChar(i32 65001, i32 0, ptr %s, i32 -1, ptr null, i32 0)
|
||||
%bad = icmp sle i32 %n, 0
|
||||
br i1 %bad, label %none, label %conv
|
||||
none:
|
||||
ret ptr null
|
||||
conv:
|
||||
%n64 = sext i32 %n to i64
|
||||
%bytes = mul i64 %n64, 2
|
||||
%w = call ptr @malloc(i64 %bytes)
|
||||
%r = call i32 @MultiByteToWideChar(i32 65001, i32 0, ptr %s, i32 -1, ptr %w, i32 %n)
|
||||
ret ptr %w
|
||||
}
|
||||
|
||||
define i32 @lu_proc_spawn(ptr %path, ptr %argv, ptr %cmdline) {
|
||||
entry:
|
||||
%si = alloca [104 x i8], align 8 ; STARTUPINFOW
|
||||
%pi = alloca [24 x i8], align 8 ; PROCESS_INFORMATION
|
||||
br label %find
|
||||
find:
|
||||
%i = phi i32 [ 0, %entry ], [ %i1, %next ]
|
||||
%stp = getelementptr [16 x i32], ptr @P_state, i32 0, i32 %i
|
||||
%st = load i32, ptr %stp
|
||||
%free = icmp eq i32 %st, 0
|
||||
br i1 %free, label %conv, label %next
|
||||
next:
|
||||
%i1 = add i32 %i, 1
|
||||
%more = icmp slt i32 %i1, 16
|
||||
br i1 %more, label %find, label %fail
|
||||
conv:
|
||||
%wpath = call ptr @pr_wide(ptr %path)
|
||||
%wcmd = call ptr @pr_wide(ptr %cmdline)
|
||||
%np = icmp eq ptr %wpath, null
|
||||
%nc = icmp eq ptr %wcmd, null
|
||||
%either = or i1 %np, %nc
|
||||
br i1 %either, label %cleanup_fail, label %spawn
|
||||
spawn:
|
||||
call void @llvm.memset.p0.i64(ptr %si, i8 0, i64 104, i1 false)
|
||||
store i32 104, ptr %si
|
||||
call void @llvm.memset.p0.i64(ptr %pi, i8 0, i64 24, i1 false)
|
||||
; CREATE_NO_WINDOW = 0x08000000; no inherited handles, the parent's environment and directory
|
||||
%ok = call i32 @CreateProcessW(ptr %wpath, ptr %wcmd, ptr null, ptr null, i32 0, i32 134217728, ptr null, ptr null, ptr %si, ptr %pi)
|
||||
%failed = icmp eq i32 %ok, 0
|
||||
br i1 %failed, label %cleanup_fail, label %keep
|
||||
keep:
|
||||
call void @free(ptr %wpath)
|
||||
call void @free(ptr %wcmd)
|
||||
%hproc = load ptr, ptr %pi
|
||||
%thp = getelementptr i8, ptr %pi, i64 8
|
||||
%hthr = load ptr, ptr %thp
|
||||
%c = call i32 @CloseHandle(ptr %hthr)
|
||||
%hp = getelementptr [16 x ptr], ptr @P_handle, i32 0, i32 %i
|
||||
store ptr %hproc, ptr %hp
|
||||
store i32 1, ptr %stp
|
||||
%cp = getelementptr [16 x i32], ptr @P_code, i32 0, i32 %i
|
||||
store i32 -1, ptr %cp
|
||||
ret i32 %i
|
||||
cleanup_fail:
|
||||
%fp = icmp eq ptr %wpath, null
|
||||
br i1 %fp, label %fc, label %freep
|
||||
freep:
|
||||
call void @free(ptr %wpath)
|
||||
br label %fc
|
||||
fc:
|
||||
%fcm = icmp eq ptr %wcmd, null
|
||||
br i1 %fcm, label %fail, label %freec
|
||||
freec:
|
||||
call void @free(ptr %wcmd)
|
||||
br label %fail
|
||||
fail:
|
||||
ret i32 -1
|
||||
}
|
||||
|
||||
define i32 @lu_proc_poll(i32 %h) {
|
||||
entry:
|
||||
%code = alloca i32, align 4
|
||||
%bad = call i1 @pr_bad(i32 %h)
|
||||
br i1 %bad, label %none, label %look
|
||||
look:
|
||||
%stp = getelementptr [16 x i32], ptr @P_state, i32 0, i32 %h
|
||||
%st = load i32, ptr %stp
|
||||
%cp = getelementptr [16 x i32], ptr @P_code, i32 0, i32 %h
|
||||
%done = icmp eq i32 %st, 2
|
||||
br i1 %done, label %cached, label %isrun
|
||||
isrun:
|
||||
%run = icmp eq i32 %st, 1
|
||||
br i1 %run, label %ask, label %none
|
||||
ask:
|
||||
%hp = getelementptr [16 x ptr], ptr @P_handle, i32 0, i32 %h
|
||||
%hproc = load ptr, ptr %hp
|
||||
%w = call i32 @WaitForSingleObject(ptr %hproc, i32 0)
|
||||
%ended = icmp eq i32 %w, 0 ; WAIT_OBJECT_0
|
||||
br i1 %ended, label %getcode, label %running
|
||||
running:
|
||||
ret i32 -1
|
||||
getcode:
|
||||
store i32 255, ptr %code
|
||||
%g = call i32 @GetExitCodeProcess(ptr %hproc, ptr %code)
|
||||
%v = load i32, ptr %code
|
||||
%neg1 = icmp eq i32 %v, -1
|
||||
%v2 = select i1 %neg1, i32 255, i32 %v
|
||||
%gfail = icmp eq i32 %g, 0
|
||||
%v3 = select i1 %gfail, i32 255, i32 %v2
|
||||
store i32 %v3, ptr %cp
|
||||
store i32 2, ptr %stp
|
||||
ret i32 %v3
|
||||
cached:
|
||||
%c = load i32, ptr %cp
|
||||
ret i32 %c
|
||||
none:
|
||||
ret i32 255
|
||||
}
|
||||
|
||||
define void @lu_proc_kill(i32 %h) {
|
||||
entry:
|
||||
%bad = call i1 @pr_bad(i32 %h)
|
||||
br i1 %bad, label %out, label %look
|
||||
look:
|
||||
%stp = getelementptr [16 x i32], ptr @P_state, i32 0, i32 %h
|
||||
%st = load i32, ptr %stp
|
||||
%run = icmp eq i32 %st, 1
|
||||
br i1 %run, label %go, label %out
|
||||
go:
|
||||
%hp = getelementptr [16 x ptr], ptr @P_handle, i32 0, i32 %h
|
||||
%hproc = load ptr, ptr %hp
|
||||
%t = call i32 @TerminateProcess(ptr %hproc, i32 137)
|
||||
br label %out
|
||||
out:
|
||||
ret void
|
||||
}
|
||||
|
||||
define void @lu_proc_free(i32 %h) {
|
||||
entry:
|
||||
%bad = call i1 @pr_bad(i32 %h)
|
||||
br i1 %bad, label %out, label %look
|
||||
look:
|
||||
%stp = getelementptr [16 x i32], ptr @P_state, i32 0, i32 %h
|
||||
%st = load i32, ptr %stp
|
||||
%used = icmp ne i32 %st, 0
|
||||
br i1 %used, label %close, label %out
|
||||
close:
|
||||
%hp = getelementptr [16 x ptr], ptr @P_handle, i32 0, i32 %h
|
||||
%hproc = load ptr, ptr %hp
|
||||
%c = call i32 @CloseHandle(ptr %hproc)
|
||||
store ptr null, ptr %hp
|
||||
store i32 0, ptr %stp
|
||||
br label %out
|
||||
out:
|
||||
ret void
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue