Commit 11c53e2e authored by Kurt B. Kaiser's avatar Kurt B. Kaiser

M PyShell.py

M run.py

1. Move subprocess socket handling to a subthread - "SockThread".
2. In the subprocess, implement a queue and global completion and exit
flags.  Execute code after it is passed through the queue.  (Currently,
user code is executed in SockThread.  The next phase of development will
move the tail of the queue to MainThread.)
3. Implement an RPC message used to shut down the execution server.
4. Improve normal and exception subprocess exits.

(At this checkin a "pass loop" interrupt doesn't work on any platform.  It
will be restored for all platforms once user code execution is moved to
MainThread.)
parent e51529d7
...@@ -367,18 +367,14 @@ class ModifiedInterpreter(InteractiveInterpreter): ...@@ -367,18 +367,14 @@ class ModifiedInterpreter(InteractiveInterpreter):
except: except:
pass pass
# Kill subprocess, spawn a new one, accept connection. # Kill subprocess, spawn a new one, accept connection.
if hasattr(os, 'kill'):
# We can interrupt any loop if we can use SIGINT. This doesn't
# work in Windows, currently we can only interrupt loops doing I/O.
self.__signal_interrupt()
# XXX KBK 13Feb03 Don't close the socket until the interrupt thread
# finishes.
self.tkconsole.executing = False
try: try:
self.interrupt_subprocess()
self.shutdown_subprocess()
self.rpcclt.close() self.rpcclt.close()
os.wait() os.wait()
except: except:
pass pass
self.tkconsole.executing = False
self.spawn_subprocess() self.spawn_subprocess()
self.rpcclt.accept() self.rpcclt.accept()
self.transfer_path() self.transfer_path()
...@@ -406,16 +402,32 @@ class ModifiedInterpreter(InteractiveInterpreter): ...@@ -406,16 +402,32 @@ class ModifiedInterpreter(InteractiveInterpreter):
pass pass
def __request_interrupt(self): def __request_interrupt(self):
self.rpcclt.asynccall("exec", "interrupt_the_server", (), {}) try:
self.rpcclt.asynccall("exec", "interrupt_the_server", (), {})
except:
pass
def interrupt_subprocess(self): def interrupt_subprocess(self):
if hasattr(os, "kill"): # XXX KBK 22Mar03 Use interrupt message on all platforms for now.
# XXX if hasattr(os, "kill"):
if False:
self.__signal_interrupt() self.__signal_interrupt()
else: else:
# Windows has no os.kill(), use an RPC message. # Windows has no os.kill(), use an RPC message.
# This is async, must be done in a thread. # This is async, must be done in a thread.
threading.Thread(target=self.__request_interrupt).start() threading.Thread(target=self.__request_interrupt).start()
def __request_shutdown(self):
try:
self.rpcclt.asynccall("exec", "shutdown_the_server", (), {})
except:
pass
def shutdown_subprocess(self):
t = threading.Thread(target=self.__request_shutdown)
t.start()
t.join()
def transfer_path(self): def transfer_path(self):
self.runcommand("""if 1: self.runcommand("""if 1:
import sys as _sys import sys as _sys
...@@ -468,9 +480,10 @@ class ModifiedInterpreter(InteractiveInterpreter): ...@@ -468,9 +480,10 @@ class ModifiedInterpreter(InteractiveInterpreter):
def kill_subprocess(self): def kill_subprocess(self):
clt = self.rpcclt clt = self.rpcclt
self.rpcclt = None
if clt is not None: if clt is not None:
self.shutdown_subprocess()
clt.close() clt.close()
self.rpcclt = None
debugger = None debugger = None
......
...@@ -2,6 +2,8 @@ import sys ...@@ -2,6 +2,8 @@ import sys
import time import time
import socket import socket
import traceback import traceback
import threading
import Queue
import boolcheck import boolcheck
...@@ -10,9 +12,20 @@ import RemoteDebugger ...@@ -10,9 +12,20 @@ import RemoteDebugger
import RemoteObjectBrowser import RemoteObjectBrowser
import StackViewer import StackViewer
import rpc import rpc
import interrupt
import __main__ import __main__
# Thread shared globals: Establish a queue between a subthread (which handles
# the socket) and the main thread (which runs user code), plus global
# completion and exit flags:
server = None # RPCServer instance
queue = Queue.Queue(0)
execution_finished = False
exit_requested = False
def main(): def main():
"""Start the Python execution server in a subprocess """Start the Python execution server in a subprocess
...@@ -20,7 +33,7 @@ def main(): ...@@ -20,7 +33,7 @@ def main():
MyHandler, which inherits register/unregister methods from RPCHandler via MyHandler, which inherits register/unregister methods from RPCHandler via
the mix-in class SocketIO. the mix-in class SocketIO.
When the RPCServer svr is instantiated, the TCPServer initialization When the RPCServer 'server' is instantiated, the TCPServer initialization
creates an instance of run.MyHandler and calls its handle() method. creates an instance of run.MyHandler and calls its handle() method.
handle() instantiates a run.Executive object, passing it a reference to the handle() instantiates a run.Executive object, passing it a reference to the
MyHandler object. That reference is saved as attribute rpchandler of the MyHandler object. That reference is saved as attribute rpchandler of the
...@@ -31,15 +44,35 @@ def main(): ...@@ -31,15 +44,35 @@ def main():
register and unregister themselves. register and unregister themselves.
""" """
global queue, execution_finished, exit_requested
port = 8833 port = 8833
if sys.argv[1:]: if sys.argv[1:]:
port = int(sys.argv[1]) port = int(sys.argv[1])
sys.argv[:] = [""] sys.argv[:] = [""]
addr = ("localhost", port) sockthread = threading.Thread(target=manage_socket,
name='SockThread',
args=(('localhost', port),))
sockthread.setDaemon(True)
sockthread.start()
while 1:
try:
if exit_requested:
sys.exit()
# XXX KBK 22Mar03 eventually check queue here!
pass
time.sleep(0.05)
except KeyboardInterrupt:
##execution_finished = True
continue
def manage_socket(address):
global server, exit_requested
for i in range(6): for i in range(6):
time.sleep(i) time.sleep(i)
try: try:
svr = rpc.RPCServer(addr, MyHandler) server = rpc.RPCServer(address, MyHandler)
break break
except socket.error, err: except socket.error, err:
if i < 3: if i < 3:
...@@ -49,18 +82,21 @@ def main(): ...@@ -49,18 +82,21 @@ def main():
+ err[1] + ", retrying...." + err[1] + ", retrying...."
else: else:
print>>sys.__stderr__, "\nConnection to Idle failed, exiting." print>>sys.__stderr__, "\nConnection to Idle failed, exiting."
sys.exit() exit_requested = True
svr.handle_request() # A single request only server.handle_request() # A single request only
class MyHandler(rpc.RPCHandler): class MyHandler(rpc.RPCHandler):
def handle(self): def handle(self):
"""Override base method"""
executive = Executive(self) executive = Executive(self)
self.register("exec", executive) self.register("exec", executive)
sys.stdin = self.get_remote_proxy("stdin") sys.stdin = self.get_remote_proxy("stdin")
sys.stdout = self.get_remote_proxy("stdout") sys.stdout = self.get_remote_proxy("stdout")
sys.stderr = self.get_remote_proxy("stderr") sys.stderr = self.get_remote_proxy("stderr")
rpc.RPCHandler.handle(self) rpc.RPCHandler.getresponse(self, myseq=None, wait=0.5)
class Executive: class Executive:
...@@ -70,6 +106,25 @@ class Executive: ...@@ -70,6 +106,25 @@ class Executive:
self.calltip = CallTips.CallTips() self.calltip = CallTips.CallTips()
def runcode(self, code): def runcode(self, code):
global queue, execution_finished
execution_finished = False
queue.put(code)
# dequeue and run in subthread
self.runcode_from_queue()
while not execution_finished:
time.sleep(0.05)
def runcode_from_queue(self):
global queue, execution_finished
# poll until queue has code object, using threads, just block?
while True:
try:
code = queue.get(0)
break
except Queue.Empty:
time.sleep(0.05)
try: try:
exec code in self.locals exec code in self.locals
except: except:
...@@ -85,7 +140,10 @@ class Executive: ...@@ -85,7 +140,10 @@ class Executive:
lines = traceback.format_exception_only(typ, val) lines = traceback.format_exception_only(typ, val)
for line in lines: for line in lines:
print>>efile, line, print>>efile, line,
self.flush_stdout() execution_finished = True
else:
self.flush_stdout()
execution_finished = True
def flush_stdout(self): def flush_stdout(self):
try: try:
...@@ -126,9 +184,14 @@ class Executive: ...@@ -126,9 +184,14 @@ class Executive:
tb[i] = fn, ln, nm, line tb[i] = fn, ln, nm, line
def interrupt_the_server(self): def interrupt_the_server(self):
# XXX KBK 05Feb03 Windows requires this be done with messages and
# threads....
self.rpchandler.interrupted = True self.rpchandler.interrupted = True
##print>>sys.__stderr__, "** Interrupt main!"
interrupt.interrupt_main()
def shutdown_the_server(self):
global exit_requested
exit_requested = True
def start_the_debugger(self, gui_adap_oid): def start_the_debugger(self, gui_adap_oid):
return RemoteDebugger.start_debugger(self.rpchandler, gui_adap_oid) return RemoteDebugger.start_debugger(self.rpchandler, gui_adap_oid)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment