2010-11-12 20:08:38 -08:00
|
|
|
#!/usr/bin/python
|
|
|
|
|
#
|
|
|
|
|
# beware the jobberwack
|
|
|
|
|
#
|
2010-11-13 04:36:44 -08:00
|
|
|
import sys, os, errno, select, fcntl
|
2010-11-12 20:08:38 -08:00
|
|
|
|
2010-11-13 04:36:44 -08:00
|
|
|
_toplevel = 0
|
|
|
|
|
_mytokens = 1
|
2010-11-12 20:08:38 -08:00
|
|
|
_fds = None
|
|
|
|
|
_waitfds = {}
|
2010-11-13 04:36:44 -08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def _debug(s):
|
|
|
|
|
if 0:
|
|
|
|
|
sys.stderr.write('jwack#%d: %s' % (os.getpid(),s))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _atoi(v):
|
|
|
|
|
try:
|
|
|
|
|
return int(v or 0)
|
|
|
|
|
except ValueError:
|
|
|
|
|
return 0
|
2010-11-12 21:09:29 -08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def _release(n):
|
2010-11-13 04:36:44 -08:00
|
|
|
global _mytokens
|
|
|
|
|
_debug('release(%d)\n' % n)
|
|
|
|
|
_mytokens += n
|
|
|
|
|
if _mytokens > 1:
|
|
|
|
|
os.write(_fds[1], 't' * (_mytokens-1))
|
|
|
|
|
_mytokens = 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _try_read(fd, n):
|
2010-11-13 04:50:03 -08:00
|
|
|
# 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.
|
2010-11-13 04:36:44 -08:00
|
|
|
fcntl.fcntl(_fds[0], fcntl.F_SETFL, os.O_NONBLOCK)
|
|
|
|
|
try:
|
2010-11-13 04:50:03 -08:00
|
|
|
try:
|
|
|
|
|
b = os.read(_fds[0], 1)
|
|
|
|
|
except OSError, e:
|
|
|
|
|
if e.errno == errno.EAGAIN:
|
|
|
|
|
return ''
|
|
|
|
|
else:
|
|
|
|
|
raise
|
|
|
|
|
finally:
|
|
|
|
|
fcntl.fcntl(_fds[0], fcntl.F_SETFL, 0)
|
2010-11-13 04:36:44 -08:00
|
|
|
return b and b or None
|
2010-11-12 20:08:38 -08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def setup(maxjobs):
|
2010-11-13 04:36:44 -08:00
|
|
|
global _fds, _toplevel
|
2010-11-12 20:08:38 -08:00
|
|
|
if _fds:
|
|
|
|
|
return # already set up
|
2010-11-13 04:36:44 -08:00
|
|
|
_debug('setup(%d)\n' % maxjobs)
|
2010-11-12 20:08:38 -08:00
|
|
|
flags = ' ' + os.getenv('MAKEFLAGS', '') + ' '
|
|
|
|
|
FIND = ' --jobserver-fds='
|
|
|
|
|
ofs = flags.find(FIND)
|
|
|
|
|
if ofs >= 0:
|
|
|
|
|
s = flags[ofs+len(FIND):]
|
|
|
|
|
(arg,junk) = s.split(' ', 1)
|
|
|
|
|
(a,b) = arg.split(',', 1)
|
2010-11-13 04:36:44 -08:00
|
|
|
a = _atoi(a)
|
|
|
|
|
b = _atoi(b)
|
2010-11-12 20:08:38 -08:00
|
|
|
if a <= 0 or b <= 0:
|
|
|
|
|
raise ValueError('invalid --jobserver-fds: %r' % arg)
|
|
|
|
|
_fds = (a,b)
|
|
|
|
|
if maxjobs and not _fds:
|
|
|
|
|
# need to start a new server
|
2010-11-13 04:36:44 -08:00
|
|
|
_toplevel = maxjobs
|
2010-11-12 20:08:38 -08:00
|
|
|
_fds = os.pipe()
|
2010-11-13 04:36:44 -08:00
|
|
|
_release(maxjobs-1)
|
2010-11-12 20:08:38 -08:00
|
|
|
os.putenv('MAKEFLAGS',
|
2010-11-12 21:09:29 -08:00
|
|
|
'%s --jobserver-fds=%d,%d -j' % (os.getenv('MAKEFLAGS'),
|
2010-11-12 20:08:38 -08:00
|
|
|
_fds[0], _fds[1]))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def wait(want_token):
|
|
|
|
|
rfds = _waitfds.keys()
|
|
|
|
|
if _fds and want_token:
|
|
|
|
|
rfds.append(_fds[0])
|
|
|
|
|
r,w,x = select.select(rfds, [], [])
|
|
|
|
|
#print 'readable: %r' % r
|
|
|
|
|
for fd in r:
|
|
|
|
|
if _fds and fd == _fds[0]:
|
|
|
|
|
pass
|
|
|
|
|
else:
|
2010-11-13 04:36:44 -08:00
|
|
|
pd = _waitfds[fd]
|
|
|
|
|
_debug("done: %r\n" % pd.name)
|
|
|
|
|
_release(1)
|
|
|
|
|
os.close(fd)
|
|
|
|
|
del _waitfds[fd]
|
|
|
|
|
rv = os.waitpid(pd.pid, 0)
|
|
|
|
|
assert(rv[0] == pd.pid)
|
|
|
|
|
rv = rv[1]
|
|
|
|
|
if os.WIFEXITED(rv):
|
|
|
|
|
pd.rv = os.WEXITSTATUS(rv)
|
2010-11-12 20:08:38 -08:00
|
|
|
else:
|
2010-11-13 04:36:44 -08:00
|
|
|
pd.rv = -os.WTERMSIG(rv)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_token(reason):
|
|
|
|
|
global _mytokens
|
2010-11-12 20:08:38 -08:00
|
|
|
while 1:
|
2010-11-13 04:36:44 -08:00
|
|
|
if _mytokens >= 1:
|
|
|
|
|
_debug('(%r) used my own token...\n' % reason)
|
|
|
|
|
_mytokens -= 1
|
|
|
|
|
return
|
|
|
|
|
_debug('(%r) waiting for tokens...\n' % reason)
|
2010-11-12 20:08:38 -08:00
|
|
|
wait(want_token=1)
|
|
|
|
|
if _fds:
|
2010-11-13 04:36:44 -08:00
|
|
|
b = _try_read(_fds[0], 1)
|
|
|
|
|
if b == None:
|
|
|
|
|
raise Exception('unexpected EOF on token read')
|
2010-11-12 20:08:38 -08:00
|
|
|
if b:
|
|
|
|
|
break
|
2010-11-13 04:36:44 -08:00
|
|
|
_debug('(%r) got a token (%r).\n' % (reason, b))
|
2010-11-12 20:08:38 -08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def wait_all():
|
2010-11-13 04:36:44 -08:00
|
|
|
_debug("wait_all\n")
|
2010-11-12 20:08:38 -08:00
|
|
|
while _waitfds:
|
2010-11-13 04:36:44 -08:00
|
|
|
_debug("wait_all: wait()\n")
|
2010-11-12 20:08:38 -08:00
|
|
|
wait(want_token=0)
|
2010-11-13 04:36:44 -08:00
|
|
|
_debug("wait_all: empty list\n")
|
|
|
|
|
if _toplevel:
|
|
|
|
|
bb = ''
|
|
|
|
|
while 1:
|
|
|
|
|
b = _try_read(_fds[0], 8192)
|
|
|
|
|
bb += b
|
|
|
|
|
if not b: break
|
|
|
|
|
if len(bb) != _toplevel-1:
|
|
|
|
|
raise Exception('on exit: expected %d tokens; found only %d'
|
|
|
|
|
% (_toplevel-1, len(b)))
|
|
|
|
|
_debug("wait_all: done\n")
|
2010-11-12 20:08:38 -08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def force_return_tokens():
|
2010-11-13 04:36:44 -08:00
|
|
|
n = len(_waitfds)
|
|
|
|
|
if n:
|
|
|
|
|
_debug('%d tokens left in force_return_tokens\n' % n)
|
|
|
|
|
_debug('returning %d tokens\n' % n)
|
|
|
|
|
for k in _waitfds.keys():
|
|
|
|
|
del _waitfds[k]
|
2010-11-12 20:08:38 -08:00
|
|
|
if _fds:
|
2010-11-12 21:09:29 -08:00
|
|
|
_release(n)
|
2010-11-12 20:08:38 -08:00
|
|
|
|
|
|
|
|
|
2010-11-13 04:36:44 -08:00
|
|
|
def _pre_job(r, w, pfn):
|
2010-11-12 20:08:38 -08:00
|
|
|
os.close(r)
|
2010-11-13 04:36:44 -08:00
|
|
|
if pfn:
|
|
|
|
|
pfn()
|
2010-11-12 20:08:38 -08:00
|
|
|
|
2010-11-13 04:36:44 -08:00
|
|
|
|
|
|
|
|
class Job:
|
|
|
|
|
def __init__(self, name, pid):
|
|
|
|
|
self.name = name
|
|
|
|
|
self.pid = pid
|
|
|
|
|
self.rv = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def start_job(reason, jobfunc):
|
2010-11-12 20:08:38 -08:00
|
|
|
setup(1)
|
2010-11-13 04:36:44 -08:00
|
|
|
get_token(reason)
|
2010-11-12 20:08:38 -08:00
|
|
|
r,w = os.pipe()
|
2010-11-13 04:36:44 -08:00
|
|
|
pid = os.fork()
|
|
|
|
|
if pid == 0:
|
|
|
|
|
# child
|
|
|
|
|
os.close(r)
|
|
|
|
|
try:
|
|
|
|
|
try:
|
|
|
|
|
jobfunc()
|
|
|
|
|
os._exit(0)
|
|
|
|
|
except Exception, e:
|
|
|
|
|
sys.stderr.write("Exception: %s\n" % e)
|
|
|
|
|
finally:
|
|
|
|
|
os._exit(201)
|
|
|
|
|
# else we're the parent process
|
2010-11-12 20:08:38 -08:00
|
|
|
os.close(w)
|
2010-11-13 04:36:44 -08:00
|
|
|
pd = Job(reason, pid)
|
|
|
|
|
_waitfds[r] = pd
|
|
|
|
|
return pd
|