2015-09-24

process-wait with timeout argument

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:
  1. Create a thread with the thread entry which calls waitpid
  2. Call pthread_cond_timedwait.
    1. If this return ETIMEDOUT, then send signal to the thread and return #f
    2. 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.

No comments:

Post a Comment