Skip to content

Commit f4df5d4

Browse files
committed
process window tests, with additional features
1 parent 2d6ee44 commit f4df5d4

4 files changed

Lines changed: 389 additions & 26 deletions

File tree

lang/libs/process/src/process.ch

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -167,11 +167,21 @@ public func spawn(cfg : ProcessConfig) : CP_Result {
167167

168168
public func wait(child : *mut ChildProcess) : PR_Result {
169169
var ret = zeroed:unsafe<PR_Result>()
170-
if(!child.is_running) {
171-
var e = ProcessError.NotRunning()
172-
pr_err(e, &mut ret)
173-
return std::replace<PR_Result>(&mut ret, zeroed:unsafe<PR_Result>())
174-
} else {}
170+
// Allow wait() after kill() — process was killed but needs to be reaped.
171+
// Only reject if the process handle has already been cleaned up.
172+
comptime if(def.windows) {
173+
if(!child.is_running && child.win.h_process == null) {
174+
var e = ProcessError.NotRunning()
175+
pr_err(e, &mut ret)
176+
return std::replace<PR_Result>(&mut ret, zeroed:unsafe<PR_Result>())
177+
} else {}
178+
} else {
179+
if(!child.is_running) {
180+
var e = ProcessError.NotRunning()
181+
pr_err(e, &mut ret)
182+
return std::replace<PR_Result>(&mut ret, zeroed:unsafe<PR_Result>())
183+
} else {}
184+
}
175185
comptime if(def.windows) {
176186
var result = zeroed:unsafe<ProcessResult>()
177187
if(win_wait(child, &raw mut result)) {
@@ -209,11 +219,24 @@ public func kill(child : *mut ChildProcess, signal : int) : UT_Result {
209219
std::replace(&mut ret, Result.Err<UnitTy, ProcessError>(std::replace<ProcessError>(&mut e, ProcessError.NotRunning())))
210220
return std::replace<UT_Result>(&mut ret, zeroed:unsafe<UT_Result>())
211221
} else {}
212-
child.is_running = false;
222+
// Close pipe handles so win_read_all doesn't block on orphaned children.
223+
if(child.win.h_stdin_write != null) {
224+
CloseHandle(child.win.h_stdin_write)
225+
child.win.h_stdin_write = null
226+
}
227+
if(child.win.h_stdout_read != null) {
228+
CloseHandle(child.win.h_stdout_read)
229+
child.win.h_stdout_read = null
230+
}
231+
if(child.win.h_stderr_read != null) {
232+
CloseHandle(child.win.h_stderr_read)
233+
child.win.h_stderr_read = null
234+
}
235+
child.is_running = false
213236
std::replace(&mut ret, Result.Ok<UnitTy, ProcessError>(UnitTy{}))
214237
return std::replace<UT_Result>(&mut ret, zeroed:unsafe<UT_Result>())
215238
} else {
216-
child.is_running = false;
239+
child.is_running = false
217240
std::replace(&mut ret, Result.Ok<UnitTy, ProcessError>(UnitTy{}))
218241
return std::replace<UT_Result>(&mut ret, zeroed:unsafe<UT_Result>())
219242
}

lang/libs/process/win/win.ch

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,7 @@ public func win_execute(cfg : *ProcessConfig, out : *mut ProcessResult) : bool {
334334

335335
// Get exit code
336336
var exit_code : DWORD = 0
337+
GetExitCodeProcess(pi.hProcess, &raw mut exit_code)
337338

338339
// Close process handles
339340
CloseHandle(pi.hProcess)
@@ -412,12 +413,14 @@ public func win_spawn(cfg : *ProcessConfig, child : *mut ChildProcess) : bool {
412413
var si : STARTUPINFOA = zeroed<STARTUPINFOA>()
413414
si.cb = sizeof(STARTUPINFOA) as DWORD
414415
si.dwFlags = STARTF_USESTDHANDLES
415-
if(cfg.capture_stdout) {
416+
if(cfg.capture_stdout || cfg.merge_stdout_stderr) {
416417
si.hStdOutput = stdout_write
417418
} else {
418419
si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE)
419420
}
420-
if(cfg.capture_stderr) {
421+
if(cfg.merge_stdout_stderr) {
422+
si.hStdError = stdout_write
423+
} else if(cfg.capture_stderr) {
421424
si.hStdError = stderr_write
422425
} else {
423426
si.hStdError = GetStdHandle(STD_ERROR_HANDLE)
@@ -483,23 +486,35 @@ public func win_wait(child : *mut ChildProcess, out : *mut ProcessResult) : bool
483486
child.win.h_stdin_write = null
484487
}
485488

486-
// Read stdout
487-
if(child.win.h_stdout_read != null) {
488-
win_read_all(child.win.h_stdout_read, &raw mut stdout_data)
489-
CloseHandle(child.win.h_stdout_read)
490-
child.win.h_stdout_read = null
491-
}
489+
// Check if the process has already exited (e.g. after kill()).
490+
// If so, close pipe handles to avoid blocking on orphaned children.
491+
var already_exited = (WaitForSingleObject(child.win.h_process, 0) == WAIT_OBJECT_0)
492492

493-
// Read stderr
494-
if(child.win.h_stderr_read != null) {
495-
win_read_all(child.win.h_stderr_read, &raw mut stderr_data)
496-
CloseHandle(child.win.h_stderr_read)
497-
child.win.h_stderr_read = null
493+
if(already_exited) {
494+
// Process is dead — close pipe read handles so reads return immediately
495+
if(child.win.h_stdout_read != null) {
496+
CloseHandle(child.win.h_stdout_read)
497+
child.win.h_stdout_read = null
498+
}
499+
if(child.win.h_stderr_read != null) {
500+
CloseHandle(child.win.h_stderr_read)
501+
child.win.h_stderr_read = null
502+
}
503+
} else {
504+
// Normal case: read stdout, then stderr, then wait
505+
if(child.win.h_stdout_read != null) {
506+
win_read_all(child.win.h_stdout_read, &raw mut stdout_data)
507+
CloseHandle(child.win.h_stdout_read)
508+
child.win.h_stdout_read = null
509+
}
510+
if(child.win.h_stderr_read != null) {
511+
win_read_all(child.win.h_stderr_read, &raw mut stderr_data)
512+
CloseHandle(child.win.h_stderr_read)
513+
child.win.h_stderr_read = null
514+
}
515+
WaitForSingleObject(child.win.h_process, INFINITE)
498516
}
499517

500-
// Wait for process
501-
WaitForSingleObject(child.win.h_process, INFINITE)
502-
503518
// Get exit code
504519
var exit_code : DWORD = 0
505520
GetExitCodeProcess(child.win.h_process, &raw mut exit_code)

lang/libs/window/win/win.ch

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -729,8 +729,11 @@ public func window_title(w : *mut Window) : string {
729729
}
730730

731731
public func window_set_title(w : *mut Window, title : *char) {
732+
if(w == null) { return }
732733
w.title = string("")
733-
w.title.append_char_ptr(title)
734+
if(title != null) {
735+
w.title.append_char_ptr(title)
736+
}
734737
if(w.hwnd != null) {
735738
unsafe var wbuf : [512]ushort
736739
widen_to_buf(title, &raw mut wbuf[0], 512)
@@ -756,6 +759,7 @@ public func window_client_size(w : *mut Window) : Size {
756759
}
757760

758761
public func window_set_size(w : *mut Window, width : int, height : int) {
762+
if(w == null) { return }
759763
w.width = width
760764
w.height = height
761765
if(w.hwnd == null) {
@@ -793,6 +797,7 @@ public func window_position(w : *mut Window) : Position {
793797
}
794798

795799
public func window_set_position(w : *mut Window, x : int, y : int) {
800+
if(w == null) { return }
796801
w.x = x
797802
w.y = y
798803
if(w.hwnd != null) {
@@ -883,18 +888,18 @@ public func window_restore(w : *mut Window) {
883888
}
884889

885890
public func window_show(w : *mut Window) {
891+
w.visible = true
886892
if(w.hwnd != null) {
887893
ShowWindow(w.hwnd, SW_SHOW)
888894
UpdateWindow(w.hwnd)
889-
w.visible = true
890895
}
891896
}
892897

893898
public func window_hide(w : *mut Window) {
899+
w.visible = false
894900
if(w.hwnd != null) {
895901
ShowWindow(w.hwnd, SW_HIDE)
896902
}
897-
w.visible = false
898903
}
899904

900905
public func window_focus(w : *mut Window) {
@@ -913,6 +918,7 @@ public func window_close(w : *mut Window) {
913918
// --- visual ---
914919

915920
public func window_set_cursor(w : *mut Window, cursor : int) {
921+
if(w == null) { return }
916922
w.cursor = cursor
917923
SetCursor(cursor_for_kind(cursor))
918924
}
@@ -938,6 +944,7 @@ public func window_set_icon(w : *mut Window, path : *char) {
938944
}
939945

940946
public func window_set_opacity(w : *mut Window, opacity : double) {
947+
if(w == null) { return }
941948
w.opacity = opacity
942949
if(w.hwnd == null) {
943950
return
@@ -954,6 +961,7 @@ public func window_set_opacity(w : *mut Window, opacity : double) {
954961
}
955962

956963
public func window_set_always_on_top(w : *mut Window, on_top : bool) {
964+
if(w == null) { return }
957965
w.always_on_top = on_top
958966
if(w.hwnd != null) {
959967
if(on_top) {
@@ -965,6 +973,7 @@ public func window_set_always_on_top(w : *mut Window, on_top : bool) {
965973
}
966974

967975
public func window_set_decorated(w : *mut Window, decorated : bool) {
976+
if(w == null) { return }
968977
if(decorated == w.decorated) {
969978
return
970979
}

0 commit comments

Comments
 (0)