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