diff --git a/jwack.py b/jwack.py index 2876094..73990d0 100644 --- a/jwack.py +++ b/jwack.py @@ -1,7 +1,7 @@ # # beware the jobberwack # -import sys, os, errno, select, fcntl +import sys, os, errno, select, fcntl, signal import atoi _toplevel = 0 @@ -24,22 +24,35 @@ def _release(n): _mytokens = 1 +def _timeout(sig, frame): + pass + + def _try_read(fd, n): - # FIXME: this isn't actually safe, because GNU make can't handle it if - # the socket is nonblocking. Ugh. That means we'll have to do their - # horrible SIGCHLD hack after all. - fcntl.fcntl(_fds[0], fcntl.F_SETFL, os.O_NONBLOCK) + # using djb's suggested way of doing non-blocking reads from a blocking + # socket: http://cr.yp.to/unix/nonblock.html + # We can't just make the socket non-blocking, because we want to be + # compatible with GNU Make, and they can't handle it. + r,w,x = select.select([fd], [], [], 0) + if not r: + return '' # try again + # ok, the socket is readable - but some other process might get there + # first. We have to set an alarm() in case our read() gets stuck. + oldh = signal.signal(signal.SIGALRM, _timeout) try: + signal.alarm(1) # emergency fallback try: b = os.read(_fds[0], 1) except OSError, e: - if e.errno == errno.EAGAIN: - return '' + if e.errno in (errno.EAGAIN, errno.EINTR): + # interrupted or it was nonblocking + return '' # try again else: raise finally: - fcntl.fcntl(_fds[0], fcntl.F_SETFL, 0) - return b and b or None + signal.alarm(0) + signal.signal(signal.SIGALRM, oldh) + return b and b or None # None means EOF def setup(maxjobs):