Show More
@@ -8,7 +8,7 b' are at the bottom of the file, the rest is left untouched.' | |||
|
8 | 8 | Must be loaded with ip.load('ipy_bzr') |
|
9 | 9 | |
|
10 | 10 | """ |
|
11 | a | |
|
11 | ||
|
12 | 12 | # Copyright (C) 2004, 2005 Aaron Bentley |
|
13 | 13 | # <aaron@aaronbentley.com> |
|
14 | 14 | # |
@@ -358,18 +358,13 b' class MTInteractiveShell(InteractiveShell):' | |||
|
358 | 358 | InteractiveShell.__init__(self,name,usage,rc,user_ns, |
|
359 | 359 | user_global_ns,banner2) |
|
360 | 360 | |
|
361 | # Locking control variable. | |
|
362 | self.thread_ready = threading.Condition(threading.RLock()) | |
|
363 | 361 | |
|
364 |
# A queue to hold the code to be executed. |
|
|
365 | # enough, because uses like macros cause reentrancy. | |
|
362 | # A queue to hold the code to be executed. | |
|
366 | 363 | self.code_queue = Queue.Queue() |
|
367 | 364 | |
|
368 | 365 | # Stuff to do at closing time |
|
369 |
self._kill = |
|
|
370 | on_kill = kw.get('on_kill') | |
|
371 | if on_kill is None: | |
|
372 | on_kill = [] | |
|
366 | self._kill = None | |
|
367 | on_kill = kw.get('on_kill', []) | |
|
373 | 368 | # Check that all things to kill are callable: |
|
374 | 369 | for t in on_kill: |
|
375 | 370 | if not callable(t): |
@@ -377,18 +372,23 b' class MTInteractiveShell(InteractiveShell):' | |||
|
377 | 372 | self.on_kill = on_kill |
|
378 | 373 | # thread identity of the "worker thread" (that may execute code directly) |
|
379 | 374 | self.worker_ident = None |
|
375 | ||
|
380 | 376 | def runsource(self, source, filename="<input>", symbol="single"): |
|
381 | 377 | """Compile and run some source in the interpreter. |
|
382 | 378 | |
|
383 | 379 | Modified version of code.py's runsource(), to handle threading issues. |
|
384 | 380 | See the original for full docstring details.""" |
|
385 | ||
|
381 | ||
|
386 | 382 | global KBINT |
|
387 | 383 | |
|
388 | 384 | # If Ctrl-C was typed, we reset the flag and return right away |
|
389 | 385 | if KBINT: |
|
390 | 386 | KBINT = False |
|
391 | 387 | return False |
|
388 | ||
|
389 | if self._kill: | |
|
390 | # can't queue new code if we are being killed | |
|
391 | return True | |
|
392 | 392 | |
|
393 | 393 | try: |
|
394 | 394 | code = self.compile(source, filename, symbol) |
@@ -401,13 +401,6 b' class MTInteractiveShell(InteractiveShell):' | |||
|
401 | 401 | # Case 2 |
|
402 | 402 | return True |
|
403 | 403 | |
|
404 | # Case 3 | |
|
405 | # Store code in queue, so the execution thread can handle it. | |
|
406 | ||
|
407 | # Note that with macros and other applications, we MAY re-enter this | |
|
408 | # section, so we have to acquire the lock with non-blocking semantics, | |
|
409 | # else we deadlock. | |
|
410 | ||
|
411 | 404 | # shortcut - if we are in worker thread, or the worker thread is not running, |
|
412 | 405 | # execute directly (to allow recursion and prevent deadlock if code is run early |
|
413 | 406 | # in IPython construction) |
@@ -415,24 +408,33 b' class MTInteractiveShell(InteractiveShell):' | |||
|
415 | 408 | if self.worker_ident is None or self.worker_ident == thread.get_ident(): |
|
416 | 409 | InteractiveShell.runcode(self,code) |
|
417 | 410 | return |
|
418 | ||
|
419 | got_lock = self.thread_ready.acquire(blocking=False) | |
|
420 | self.code_queue.put(code) | |
|
421 | if got_lock: | |
|
422 | self.thread_ready.wait() # Wait until processed in timeout interval | |
|
423 | self.thread_ready.release() | |
|
424 | 411 | |
|
412 | # Case 3 | |
|
413 | # Store code in queue, so the execution thread can handle it. | |
|
414 | ||
|
415 | completed_ev, received_ev = threading.Event(), threading.Event() | |
|
416 | ||
|
417 | self.code_queue.put((code,completed_ev, received_ev)) | |
|
418 | # first make sure the message was received, with timeout | |
|
419 | received_ev.wait(5) | |
|
420 | if not received_ev.isSet(): | |
|
421 | # the mainloop is dead, start executing code directly | |
|
422 | print "Warning: Timeout for mainloop thread exceeded" | |
|
423 | print "switching to nonthreaded mode (until mainloop wakes up again)" | |
|
424 | self.worker_ident = None | |
|
425 | else: | |
|
426 | completed_ev.wait() | |
|
425 | 427 | return False |
|
426 | 428 | |
|
427 | 429 | def runcode(self): |
|
428 | 430 | """Execute a code object. |
|
429 | 431 | |
|
430 | 432 | Multithreaded wrapper around IPython's runcode().""" |
|
431 | ||
|
433 | ||
|
432 | 434 | global CODE_RUN |
|
433 | # lock thread-protected stuff | |
|
435 | ||
|
436 | # we are in worker thread, stash out the id for runsource() | |
|
434 | 437 | self.worker_ident = thread.get_ident() |
|
435 | got_lock = self.thread_ready.acquire() | |
|
436 | 438 | |
|
437 | 439 | if self._kill: |
|
438 | 440 | print >>Term.cout, 'Closing threads...', |
@@ -440,6 +442,9 b' class MTInteractiveShell(InteractiveShell):' | |||
|
440 | 442 | for tokill in self.on_kill: |
|
441 | 443 | tokill() |
|
442 | 444 | print >>Term.cout, 'Done.' |
|
445 | # allow kill() to return | |
|
446 | self._kill.set() | |
|
447 | return True | |
|
443 | 448 | |
|
444 | 449 | # Install sigint handler. We do it every time to ensure that if user |
|
445 | 450 | # code modifies it, we restore our own handling. |
@@ -455,9 +460,11 b' class MTInteractiveShell(InteractiveShell):' | |||
|
455 | 460 | code_to_run = None |
|
456 | 461 | while 1: |
|
457 | 462 | try: |
|
458 | code_to_run = self.code_queue.get_nowait() | |
|
463 | code_to_run, completed_ev, received_ev = self.code_queue.get_nowait() | |
|
459 | 464 | except Queue.Empty: |
|
460 | 465 | break |
|
466 | received_ev.set() | |
|
467 | ||
|
461 | 468 | # Exceptions need to be raised differently depending on which |
|
462 | 469 | # thread is active. This convoluted try/except is only there to |
|
463 | 470 | # protect against asynchronous exceptions, to ensure that a KBINT |
@@ -471,28 +478,23 b' class MTInteractiveShell(InteractiveShell):' | |||
|
471 | 478 | except KeyboardInterrupt: |
|
472 | 479 | print "Keyboard interrupted in mainloop" |
|
473 | 480 | while not self.code_queue.empty(): |
|
474 | self.code_queue.get_nowait() | |
|
481 | code, ev1,ev2 = self.code_queue.get_nowait() | |
|
482 | ev1.set() | |
|
483 | ev2.set() | |
|
475 | 484 | break |
|
476 | 485 | finally: |
|
477 |
|
|
|
478 | CODE_RUN = False | |
|
479 | ||
|
480 | # We're done with thread-protected variables | |
|
481 | if code_to_run is not None: | |
|
482 | self.thread_ready.notify() | |
|
483 | self.thread_ready.release() | |
|
484 | ||
|
485 | # We're done... | |
|
486 | CODE_RUN = False | |
|
486 | CODE_RUN = False | |
|
487 | # allow runsource() return from wait | |
|
488 | completed_ev.set() | |
|
489 | ||
|
490 | ||
|
487 | 491 | # This MUST return true for gtk threading to work |
|
488 | 492 | return True |
|
489 | 493 | |
|
490 | 494 | def kill(self): |
|
491 | 495 | """Kill the thread, returning when it has been shut down.""" |
|
492 | got_lock = self.thread_ready.acquire(False) | |
|
493 |
self._kill |
|
|
494 | if got_lock: | |
|
495 | self.thread_ready.release() | |
|
496 | self._kill = threading.Event() | |
|
497 | self._kill.wait() | |
|
496 | 498 | |
|
497 | 499 | class MatplotlibShellBase: |
|
498 | 500 | """Mixin class to provide the necessary modifications to regular IPython |
General Comments 0
You need to be logged in to leave comments.
Login now