Need non-blocking way to know if child process failed
Affects | Status | Importance | Assigned to | Milestone | |
---|---|---|---|---|---|
Ikarus Scheme |
Fix Committed
|
High
|
Abdulaziz Ghuloum |
Bug Description
Currently, there's no non-blocking way to test if a child process succeeded or not. The only way is to try reading from the err port to see what string it returns, but this might block forever, and then waitpid returns weird values because waitpid(2) packs various info in the status.
Ikarus Scheme version 0.0.2patched+ (revision 1366, build 2008-01-23)
Copyright (c) 2006-2008 Abdulaziz Ghuloum
> (define-values (pid to from err) (process "asdfasdf"))
> (define errt (transcoded-port err (native-
> (get-string-all errt) ;; But I don't know if this will block
"failed to exec asdfasdf: No such file or directory\n"
> (waitpid pid)
65280
> (waitpid pid)
-141090988
> (waitpid pid)
-141091004
> (waitpid pid)
-141091020
> (waitpid pid)
-141091036
> (waitpid pid)
-141091052
> (waitpid pid)
-141091068
> (waitpid pid)
-141091084
>
To be able to test if executing the process succeeded, I made the following changes:
[d@eep:
=== modified file 'scheme/
--- scheme/
+++ scheme/
@@ -37,10 +37,15 @@
[else (parent-proc pid)]))))
(define waitpid
- (lambda (pid)
- (unless (fixnum? pid)
- (die 'waitpid "not a fixnum" pid))
- (foreign-call "ikrt_waitpid" pid)))
+ (case-lambda
+ [(pid)
+ (waitpid pid #t)]
+ [(pid block?)
+ (unless (fixnum? pid)
+ (die 'waitpid "not a fixnum" pid))
+ (unless (boolean? block?)
+ (die 'waitpid "not a boolean" block?))
+ (foreign-call "ikrt_waitpid" pid block?)]))
(define system
(lambda (x)
=== modified file 'src/ikarus-
--- src/ikarus-
+++ src/ikarus-
@@ -74,8 +74,15 @@
}
ikptr
-ikrt_waitpid(ikptr pid, ikpcb* pcb){
+ikrt_waitpid(ikptr pid, ikptr block, ikpcb* pcb){
int status;
- waitpid(unfix(pid), &status, 0);
- return fix(status);
+ int options = 0;
+ int r;
+ if (block == false_object)
+ options = WNOHANG;
+ r = waitpid(unfix(pid), &status, options);
+ if (r > 0 && r == unfix(pid) && WIFEXITED(status))
+ return fix(WEXITSTATUS
+ else
+ return false_object;
}
Maybe this is a good way to accomplish this. I disclaim all copyright to this patch. Do what thou wilt with it :)
With this patch, I can do:
Ikarus Scheme version 0.0.2patched+ (revision 1366, build 2008-01-24)
Copyright (c) 2006-2008 Abdulaziz Ghuloum
> (define-values (pid to from err) (process "asdfasdf"))
> (waitpid pid #f) ;; Don't block
255 ;; Oh no, the forked process already exited
> (waitpid pid) ;; Just to demonstrate
#f ;; means this waitpid test did not show the process exited normally (it was already tested)
> (define errt (transcoded-port err (native-
> (get-string-all errt)
"failed to exec asdfasdf: No such file or directory\n"
>
And:
Ikarus Scheme version 0.0.2patched+ (revision 1366, build 2008-01-24)
Copyright (c) 2006-2008 Abdulaziz Ghuloum
> (define-values (pid to from err) (process "cat"))
> (waitpid pid #f) ;; Don't block
#f ;; It's alive
> (define tot (transcoded-port to (native-
> (define fromt (transcoded-port from (native-
> (put-string tot "blah")
> (flush-output-port tot)
> (get-string-n fromt 4)
"blah"
> (close-port tot)
> (close-port fromt)
> (close-port err)
> (waitpid pid) ;; old blocking behavior
0
>
If my patch isn't the right thing, I request some non-blocking way to test if the executed process died or not.
Changed in ikarus: | |
milestone: | none → 0.0.4 |
I agree this needs to be fixed. The solution that you posted has a problem with a race condition since by the time you do you (waitpid pid #f), the child may not have gotten a chance to exec, fail, and exit. I'll have to check with Steven's on the best way to do this.