Recently, I'm writing own CI task on my local machine. (it doesn't mean I quit others when it's completed but just for fun.) Then, I've noticed that it might be convenient to have timeout argument on
process-wait
instead of using timer to poll every 500 ms (or whatever). So I've implemented it, now you can use like this:
(import (rnrs) (sagittarius process))
(let ((p (call "sh" "sleep_forever.sh")))
;; wait for 5 seconds. can also be time object.
(unless (process-wait p :timeout 5))
;; it returns #f if timed out. so kill it
(process-kill p)))
Now, I don't have to make timer to check. Very convenient :)
TL;DR
From here is the description of underlying implementation. This is not something you may be interested in but simply for my memo (and hope that somebody would indicate better solusion).
Sagittarius supports both Windows and POSIX environment. On Windows, the story was very simple, I just needed to call
WaitForSingleObject
with process handle and timeout millisecond. Hurray done!. I like this abstraction that API can handle almost any kind of handle. (Most of the time it's a headache to support Windows but sometimes it's easier than POSIX.)
On POSIX, on the other hand, it doesn't have
timedwaitpid
or something like that. I've asked the teacher, Google, what would be the solution. The first hit was this:
Waitpid equivalent with timeout?. Its best answer doesn't seem the best answer since entire process would be affected. Plus, it would always wait for specified timeout amount of time which is not something I want. The most useful pointed one looks nice if POSIX has a portable way to get standard output file descriptor from pid. Though, there seems a way to do it using
ptrace(2)
. (See
reptyr(1)
and
its source). I haven't looked into its detail but, in my understanding, it opens given pid process with
ptrace(2)
and calls
dup(2)
on that process (
ptrace_remote_syscall
does this black magic I think). It'd be nice to have it if I can do this all platforms Sagittarius supports. However not an easy task to do it without having real environments (e.g. OS X is only on Travis CI).
Go easy
What I wanted to do is waitpid
with timeout. So I just need to have a watch dog which alerts either timeout is happened or the target process is finished. Well, the easiest one I could think of was thread with pthread_cond_timedwait(2)
.
The idea is pretty much simple. It goes the following steps:
- Create a thread with the thread entry which calls
waitpid
- Call
pthread_cond_timedwait
.
- If this return
ETIMEDOUT
, then send signal to the thread and return #f
- Otherwise return the return code.
Done! What an easy solution! (There was a stupid mistake. I didn't know pthread_join
can't be called with detached thread. Though, interestingly, it worked on FreeBSD, why?)
Issue
Well, if I can say this is a perfect solution, I'd be super happy but of cource this has an issue (for now only one I'm facing!). Because of the limitation of waitpid (2)
, the pid can be waited must be created by the parent process means Sagittarius script. Thus processes created by pid->process
can't be passed (well this is not only for timeout argument but entire procedure issue, though). If this happens, then ECHILD
is raised (see waitpid(2)
).
I haven't got any problem with it but if would, then I need a better solution (maybe set PPID to running process? but how?). But for now, I'm happy enough and I believe there's very few situation which users want to wait non child processes.
Again, this isn't an issue on Windows. I don't care good or not but I like it.