diff options
Diffstat (limited to 'bitbake/lib/bb/server/process.py')
-rw-r--r-- | bitbake/lib/bb/server/process.py | 105 |
1 files changed, 68 insertions, 37 deletions
diff --git a/bitbake/lib/bb/server/process.py b/bitbake/lib/bb/server/process.py index 2aee9ef051..ac7749d36c 100644 --- a/bitbake/lib/bb/server/process.py +++ b/bitbake/lib/bb/server/process.py | |||
@@ -92,8 +92,11 @@ class ProcessServer(): | |||
92 | self.maxuiwait = 30 | 92 | self.maxuiwait = 30 |
93 | self.xmlrpc = False | 93 | self.xmlrpc = False |
94 | 94 | ||
95 | self.idle = None | ||
96 | # Need a lock for _idlefuns changes | ||
95 | self._idlefuns = {} | 97 | self._idlefuns = {} |
96 | self._idlefuncsLock = threading.Lock() | 98 | self._idlefuncsLock = threading.Lock() |
99 | self.idle_cond = threading.Condition(self._idlefuncsLock) | ||
97 | 100 | ||
98 | self.bitbake_lock = lock | 101 | self.bitbake_lock = lock |
99 | self.bitbake_lock_name = lockname | 102 | self.bitbake_lock_name = lockname |
@@ -151,6 +154,12 @@ class ProcessServer(): | |||
151 | 154 | ||
152 | return ret | 155 | return ret |
153 | 156 | ||
157 | def wait_for_idle(self, timeout=30): | ||
158 | # Wait for the idle loop to have cleared | ||
159 | with self.idle_cond: | ||
160 | # FIXME - the 1 is the inotify processing in cooker which always runs | ||
161 | self.idle_cond.wait_for(lambda: len(self._idlefuns) <= 1, timeout) | ||
162 | |||
154 | def main(self): | 163 | def main(self): |
155 | self.cooker.pre_serve() | 164 | self.cooker.pre_serve() |
156 | 165 | ||
@@ -174,6 +183,12 @@ class ProcessServer(): | |||
174 | self.controllersock.close() | 183 | self.controllersock.close() |
175 | self.controllersock = False | 184 | self.controllersock = False |
176 | if self.haveui: | 185 | if self.haveui: |
186 | # Wait for the idle loop to have cleared (30s max) | ||
187 | self.wait_for_idle(30) | ||
188 | if self.cooker.command.currentAsyncCommand is not None: | ||
189 | serverlog("Idle loop didn't finish queued commands after 30s, exiting.") | ||
190 | self.quit = True | ||
191 | |||
177 | fds.remove(self.command_channel) | 192 | fds.remove(self.command_channel) |
178 | bb.event.unregister_UIHhandler(self.event_handle, True) | 193 | bb.event.unregister_UIHhandler(self.event_handle, True) |
179 | self.command_channel_reply.writer.close() | 194 | self.command_channel_reply.writer.close() |
@@ -185,7 +200,7 @@ class ProcessServer(): | |||
185 | self.cooker.clientComplete() | 200 | self.cooker.clientComplete() |
186 | self.haveui = False | 201 | self.haveui = False |
187 | ready = select.select(fds,[],[],0)[0] | 202 | ready = select.select(fds,[],[],0)[0] |
188 | if newconnections: | 203 | if newconnections and not self.quit: |
189 | serverlog("Starting new client") | 204 | serverlog("Starting new client") |
190 | conn = newconnections.pop(-1) | 205 | conn = newconnections.pop(-1) |
191 | fds.append(conn) | 206 | fds.append(conn) |
@@ -257,7 +272,7 @@ class ProcessServer(): | |||
257 | continue | 272 | continue |
258 | try: | 273 | try: |
259 | serverlog("Running command %s" % command) | 274 | serverlog("Running command %s" % command) |
260 | self.command_channel_reply.send(self.cooker.command.runCommand(command)) | 275 | self.command_channel_reply.send(self.cooker.command.runCommand(command, self)) |
261 | serverlog("Command Completed (socket: %s)" % os.path.exists(self.sockname)) | 276 | serverlog("Command Completed (socket: %s)" % os.path.exists(self.sockname)) |
262 | except Exception as e: | 277 | except Exception as e: |
263 | stack = traceback.format_exc() | 278 | stack = traceback.format_exc() |
@@ -285,6 +300,9 @@ class ProcessServer(): | |||
285 | 300 | ||
286 | ready = self.idle_commands(.1, fds) | 301 | ready = self.idle_commands(.1, fds) |
287 | 302 | ||
303 | if self.idle: | ||
304 | self.idle.join() | ||
305 | |||
288 | serverlog("Exiting (socket: %s)" % os.path.exists(self.sockname)) | 306 | serverlog("Exiting (socket: %s)" % os.path.exists(self.sockname)) |
289 | # Remove the socket file so we don't get any more connections to avoid races | 307 | # Remove the socket file so we don't get any more connections to avoid races |
290 | # The build directory could have been renamed so if the file isn't the one we created | 308 | # The build directory could have been renamed so if the file isn't the one we created |
@@ -300,7 +318,7 @@ class ProcessServer(): | |||
300 | self.sock.close() | 318 | self.sock.close() |
301 | 319 | ||
302 | try: | 320 | try: |
303 | self.cooker.shutdown(True) | 321 | self.cooker.shutdown(True, idle=False) |
304 | self.cooker.notifier.stop() | 322 | self.cooker.notifier.stop() |
305 | self.cooker.confignotifier.stop() | 323 | self.cooker.confignotifier.stop() |
306 | except: | 324 | except: |
@@ -359,47 +377,60 @@ class ProcessServer(): | |||
359 | msg.append(":\n%s" % procs) | 377 | msg.append(":\n%s" % procs) |
360 | serverlog("".join(msg)) | 378 | serverlog("".join(msg)) |
361 | 379 | ||
362 | def idle_commands(self, delay, fds=None): | 380 | def idle_thread(self): |
363 | def remove_idle_func(function): | 381 | def remove_idle_func(function): |
364 | with self._idlefuncsLock: | 382 | with self._idlefuncsLock: |
365 | del self._idlefuns[function] | 383 | del self._idlefuns[function] |
384 | self.idle_cond.notify_all() | ||
366 | 385 | ||
367 | nextsleep = delay | 386 | while not self.quit: |
368 | if not fds: | 387 | nextsleep = 0.1 |
369 | fds = [] | 388 | fds = [] |
370 | 389 | ||
371 | with self._idlefuncsLock: | 390 | with self._idlefuncsLock: |
372 | items = list(self._idlefuns.items()) | 391 | items = list(self._idlefuns.items()) |
373 | 392 | ||
374 | for function, data in items: | 393 | for function, data in items: |
375 | try: | 394 | try: |
376 | retval = function(self, data, False) | 395 | retval = function(self, data, False) |
377 | if isinstance(retval, idleFinish): | 396 | if isinstance(retval, idleFinish): |
378 | serverlog("Removing idle function %s at idleFinish" % str(function)) | 397 | serverlog("Removing idle function %s at idleFinish" % str(function)) |
379 | remove_idle_func(function) | 398 | remove_idle_func(function) |
380 | self.cooker.command.finishAsyncCommand(retval.msg) | 399 | self.cooker.command.finishAsyncCommand(retval.msg) |
381 | nextsleep = None | 400 | nextsleep = None |
382 | elif retval is False: | 401 | elif retval is False: |
383 | serverlog("Removing idle function %s" % str(function)) | 402 | serverlog("Removing idle function %s" % str(function)) |
403 | remove_idle_func(function) | ||
404 | nextsleep = None | ||
405 | elif retval is True: | ||
406 | nextsleep = None | ||
407 | elif isinstance(retval, float) and nextsleep: | ||
408 | if (retval < nextsleep): | ||
409 | nextsleep = retval | ||
410 | elif nextsleep is None: | ||
411 | continue | ||
412 | else: | ||
413 | fds = fds + retval | ||
414 | except SystemExit: | ||
415 | raise | ||
416 | except Exception as exc: | ||
417 | if not isinstance(exc, bb.BBHandledException): | ||
418 | logger.exception('Running idle function') | ||
384 | remove_idle_func(function) | 419 | remove_idle_func(function) |
385 | nextsleep = None | 420 | serverlog("Exception %s broke the idle_thread, exiting" % traceback.format_exc()) |
386 | elif retval is True: | 421 | self.quit = True |
387 | nextsleep = None | 422 | |
388 | elif isinstance(retval, float) and nextsleep: | 423 | if nextsleep is not None: |
389 | if (retval < nextsleep): | 424 | select.select(fds,[],[],nextsleep)[0] |
390 | nextsleep = retval | 425 | |
391 | elif nextsleep is None: | 426 | def idle_commands(self, delay, fds=None): |
392 | continue | 427 | nextsleep = delay |
393 | else: | 428 | if not fds: |
394 | fds = fds + retval | 429 | fds = [] |
395 | except SystemExit: | 430 | |
396 | raise | 431 | if not self.idle: |
397 | except Exception as exc: | 432 | self.idle = threading.Thread(target=self.idle_thread) |
398 | if not isinstance(exc, bb.BBHandledException): | 433 | self.idle.start() |
399 | logger.exception('Running idle function') | ||
400 | remove_idle_func(function) | ||
401 | serverlog("Exception %s broke the idle_thread, exiting" % traceback.format_exc()) | ||
402 | self.quit = True | ||
403 | 434 | ||
404 | # Create new heartbeat event? | 435 | # Create new heartbeat event? |
405 | now = time.time() | 436 | now = time.time() |
@@ -592,7 +623,7 @@ def execServer(lockfd, readypipeinfd, lockname, sockname, server_timeout, xmlrpc | |||
592 | writer = ConnectionWriter(readypipeinfd) | 623 | writer = ConnectionWriter(readypipeinfd) |
593 | try: | 624 | try: |
594 | featureset = [] | 625 | featureset = [] |
595 | cooker = bb.cooker.BBCooker(featureset, server.register_idle_function) | 626 | cooker = bb.cooker.BBCooker(featureset, server.register_idle_function, server.wait_for_idle) |
596 | cooker.configuration.profile = profile | 627 | cooker.configuration.profile = profile |
597 | except bb.BBHandledException: | 628 | except bb.BBHandledException: |
598 | return None | 629 | return None |