jack2 codebase
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

242 lines
6.1KB

  1. #! /usr/bin/env python
  2. # encoding: utf-8
  3. # Thomas Nagy, 2015 (ita)
  4. # TODO: have the child process terminate if the parent is killed abruptly
  5. import os, re, socket, threading, sys, subprocess, time, atexit, traceback, random
  6. try:
  7. import SocketServer
  8. except ImportError:
  9. import socketserver as SocketServer
  10. try:
  11. from queue import Queue
  12. except ImportError:
  13. from Queue import Queue
  14. import json as pickle
  15. SHARED_KEY = None
  16. HEADER_SIZE = 64
  17. REQ = 'REQ'
  18. RES = 'RES'
  19. BYE = 'BYE'
  20. def make_header(params, cookie=''):
  21. header = ','.join(params)
  22. header = header.ljust(HEADER_SIZE - len(cookie))
  23. assert(len(header) == HEADER_SIZE - len(cookie))
  24. header = header + cookie
  25. if sys.hexversion > 0x3000000:
  26. header = header.encode('iso8859-1')
  27. return header
  28. if 1:
  29. from waflib import Logs, Utils, Runner, Errors, Options
  30. def init_task_pool(self):
  31. # lazy creation, and set a common pool for all task consumers
  32. pool = self.pool = []
  33. for i in range(self.numjobs):
  34. consumer = Runner.get_pool()
  35. pool.append(consumer)
  36. consumer.idx = i
  37. self.ready = Queue(0)
  38. def setq(consumer):
  39. consumer.ready = self.ready
  40. try:
  41. threading.current_thread().idx = consumer.idx
  42. except Exception as e:
  43. print(e)
  44. for x in pool:
  45. x.ready.put(setq)
  46. return pool
  47. Runner.Parallel.init_task_pool = init_task_pool
  48. def make_server(bld, idx):
  49. top = getattr(bld, 'preforkjava_top', os.path.dirname(os.path.abspath('__file__')))
  50. cp = getattr(bld, 'preforkjava_cp', os.path.join(top, 'minimal-json-0.9.3-SNAPSHOT.jar') + os.pathsep + top)
  51. for x in cp.split(os.pathsep):
  52. if x and not os.path.exists(x):
  53. Logs.warn('Invalid classpath: %r' % cp)
  54. Logs.warn('Set for example bld.preforkjava_cp to /path/to/minimal-json:/path/to/Prefork.class/')
  55. cwd = getattr(bld, 'preforkjava_cwd', top)
  56. port = getattr(bld, 'preforkjava_port', 51200)
  57. cmd = getattr(bld, 'preforkjava_cmd', 'java -cp %s%s Prefork %d' % (cp, os.pathsep, port))
  58. proc = subprocess.Popen(cmd.split(), shell=False, cwd=cwd)
  59. proc.port = port
  60. return proc
  61. def make_conn(bld, srv):
  62. #port = PORT + idx
  63. port = srv.port
  64. conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  65. conn.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
  66. conn.connect(('127.0.0.1', port))
  67. return conn
  68. SERVERS = []
  69. CONNS = []
  70. def close_all():
  71. global SERVERS
  72. while SERVERS:
  73. srv = SERVERS.pop()
  74. pid = srv.pid
  75. try:
  76. srv.kill()
  77. except Exception as e:
  78. pass
  79. atexit.register(close_all)
  80. def put_data(conn, data):
  81. cnt = 0
  82. while cnt < len(data):
  83. sent = conn.send(data[cnt:])
  84. if sent == 0:
  85. raise RuntimeError('connection ended')
  86. cnt += sent
  87. def read_data(conn, siz):
  88. cnt = 0
  89. buf = []
  90. while cnt < siz:
  91. data = conn.recv(min(siz - cnt, 1024))
  92. if not data:
  93. raise RuntimeError('connection ended %r %r' % (cnt, siz))
  94. buf.append(data)
  95. cnt += len(data)
  96. if sys.hexversion > 0x3000000:
  97. ret = ''.encode('iso8859-1').join(buf)
  98. else:
  99. ret = ''.join(buf)
  100. return ret
  101. def exec_command(self, cmd, **kw):
  102. if 'stdout' in kw:
  103. if kw['stdout'] not in (None, subprocess.PIPE):
  104. return self.exec_command_old(cmd, **kw)
  105. elif 'stderr' in kw:
  106. if kw['stderr'] not in (None, subprocess.PIPE):
  107. return self.exec_command_old(cmd, **kw)
  108. kw['shell'] = isinstance(cmd, str)
  109. Logs.debug('runner: %r' % cmd)
  110. Logs.debug('runner_env: kw=%s' % kw)
  111. if self.logger:
  112. self.logger.info(cmd)
  113. if 'stdout' not in kw:
  114. kw['stdout'] = subprocess.PIPE
  115. if 'stderr' not in kw:
  116. kw['stderr'] = subprocess.PIPE
  117. if Logs.verbose and not kw['shell'] and not Utils.check_exe(cmd[0]):
  118. raise Errors.WafError("Program %s not found!" % cmd[0])
  119. idx = threading.current_thread().idx
  120. kw['cmd'] = cmd
  121. data = pickle.dumps(kw)
  122. params = [REQ, str(len(data))]
  123. header = make_header(params, self.SHARED_KEY)
  124. conn = CONNS[idx]
  125. if sys.hexversion > 0x3000000:
  126. data = data.encode('iso8859-1')
  127. put_data(conn, header + data)
  128. data = read_data(conn, HEADER_SIZE)
  129. if sys.hexversion > 0x3000000:
  130. data = data.decode('iso8859-1')
  131. #print("received %r" % data)
  132. lst = data.split(',')
  133. ret = int(lst[1])
  134. dlen = int(lst[2])
  135. out = err = None
  136. if dlen:
  137. data = read_data(conn, dlen)
  138. (out, err, exc) = pickle.loads(data)
  139. if exc:
  140. raise Errors.WafError('Execution failure: %s' % exc)
  141. if out:
  142. if not isinstance(out, str):
  143. out = out.decode(sys.stdout.encoding or 'iso8859-1')
  144. if self.logger:
  145. self.logger.debug('out: %s' % out)
  146. else:
  147. Logs.info(out, extra={'stream':sys.stdout, 'c1': ''})
  148. if err:
  149. if not isinstance(err, str):
  150. err = err.decode(sys.stdout.encoding or 'iso8859-1')
  151. if self.logger:
  152. self.logger.error('err: %s' % err)
  153. else:
  154. Logs.info(err, extra={'stream':sys.stderr, 'c1': ''})
  155. return ret
  156. def init_key(ctx):
  157. try:
  158. key = ctx.SHARED_KEY = os.environ['SHARED_KEY']
  159. except KeyError:
  160. key = "".join([chr(random.SystemRandom().randint(40, 126)) for x in range(20)])
  161. os.environ['SHARED_KEY'] = ctx.SHARED_KEY = key
  162. os.environ['PREFORKPID'] = str(os.getpid())
  163. return key
  164. def init_servers(ctx, maxval):
  165. while len(SERVERS) < 1:
  166. i = len(SERVERS)
  167. srv = make_server(ctx, i)
  168. SERVERS.append(srv)
  169. while len(CONNS) < maxval:
  170. i = len(CONNS)
  171. srv = SERVERS[0]
  172. conn = None
  173. for x in range(30):
  174. try:
  175. conn = make_conn(ctx, srv)
  176. break
  177. except socket.error:
  178. time.sleep(0.01)
  179. if not conn:
  180. raise ValueError('Could not start the server!')
  181. CONNS.append(conn)
  182. def init_smp(self):
  183. if not getattr(Options.options, 'smp', getattr(self, 'smp', None)):
  184. return
  185. if Utils.unversioned_sys_platform() in ('freebsd',):
  186. pid = os.getpid()
  187. cmd = ['cpuset', '-l', '0', '-p', str(pid)]
  188. elif Utils.unversioned_sys_platform() in ('linux',):
  189. pid = os.getpid()
  190. cmd = ['taskset', '-pc', '0', str(pid)]
  191. if cmd:
  192. self.cmd_and_log(cmd, quiet=0)
  193. def options(opt):
  194. opt.add_option('--pin-process', action='store_true', dest='smp', default=False)
  195. init_key(opt)
  196. init_servers(opt, 40)
  197. def build(bld):
  198. if bld.cmd == 'clean':
  199. return
  200. init_key(bld)
  201. init_servers(bld, bld.jobs)
  202. init_smp(bld)
  203. bld.__class__.exec_command_old = bld.__class__.exec_command
  204. bld.__class__.exec_command = exec_command