]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
M PyShell.py
authorKurt B. Kaiser <kbk@shore.net>
Sat, 22 Mar 2003 19:40:19 +0000 (19:40 +0000)
committerKurt B. Kaiser <kbk@shore.net>
Sat, 22 Mar 2003 19:40:19 +0000 (19:40 +0000)
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.)

Lib/idlelib/PyShell.py
Lib/idlelib/run.py

index 0c9fbfbcfac20eca85b6a2c644a102946879e7ec..9f158f5a90a2c58ef7621a4d5ee8294fc1e9ebfe 100644 (file)
@@ -367,18 +367,14 @@ class ModifiedInterpreter(InteractiveInterpreter):
             except:
                 pass
         # 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:
+            self.interrupt_subprocess()
+            self.shutdown_subprocess()
             self.rpcclt.close()
             os.wait()
         except:
             pass
+        self.tkconsole.executing = False
         self.spawn_subprocess()
         self.rpcclt.accept()
         self.transfer_path()
@@ -406,16 +402,32 @@ class ModifiedInterpreter(InteractiveInterpreter):
             pass
 
     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):
-        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()
         else:
             # Windows has no os.kill(), use an RPC message.
             # This is async, must be done in a thread.
             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):
         self.runcommand("""if 1:
         import sys as _sys
@@ -468,9 +480,10 @@ class ModifiedInterpreter(InteractiveInterpreter):
 
     def kill_subprocess(self):
         clt = self.rpcclt
-        self.rpcclt = None
         if clt is not None:
+            self.shutdown_subprocess()
             clt.close()
+        self.rpcclt = None
 
     debugger = None
 
index d79f3d73d0cf848c0381c73d74d9ecc477415e91..497cbbd622be457311293f05c27483f423b6b2dc 100644 (file)
@@ -2,6 +2,8 @@ import sys
 import time
 import socket
 import traceback
+import threading
+import Queue
 
 import boolcheck
 
@@ -10,9 +12,20 @@ import RemoteDebugger
 import RemoteObjectBrowser
 import StackViewer
 import rpc
+import interrupt
 
 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():
     """Start the Python execution server in a subprocess
 
@@ -20,7 +33,7 @@ def main():
     MyHandler, which inherits register/unregister methods from RPCHandler via
     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.
     handle() instantiates a run.Executive object, passing it a reference to the
     MyHandler object.  That reference is saved as attribute rpchandler of the
@@ -31,15 +44,35 @@ def main():
     register and unregister themselves.
 
     """
+    global queue, execution_finished, exit_requested
+
     port = 8833
     if sys.argv[1:]:
         port = int(sys.argv[1])
     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):
         time.sleep(i)
         try:
-            svr = rpc.RPCServer(addr, MyHandler)
+            server = rpc.RPCServer(address, MyHandler)
             break
         except socket.error, err:
             if i < 3:
@@ -49,18 +82,21 @@ def main():
                                               + err[1] + ", retrying...."
     else:
         print>>sys.__stderr__, "\nConnection to Idle failed, exiting."
-        sys.exit()
-    svr.handle_request() # A single request only
+        exit_requested = True
+    server.handle_request() # A single request only
+
 
 class MyHandler(rpc.RPCHandler):
 
     def handle(self):
+        """Override base method"""
         executive = Executive(self)
         self.register("exec", executive)
         sys.stdin = self.get_remote_proxy("stdin")
         sys.stdout = self.get_remote_proxy("stdout")
         sys.stderr = self.get_remote_proxy("stderr")
-        rpc.RPCHandler.handle(self)
+        rpc.RPCHandler.getresponse(self, myseq=None, wait=0.5)
+
 
 class Executive:
 
@@ -70,6 +106,25 @@ class Executive:
         self.calltip = CallTips.CallTips()
 
     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:
             exec code in self.locals
         except:
@@ -85,7 +140,10 @@ class Executive:
             lines = traceback.format_exception_only(typ, val)
             for line in lines:
                 print>>efile, line,
-        self.flush_stdout()
+            execution_finished = True
+        else:
+            self.flush_stdout()
+            execution_finished = True
 
     def flush_stdout(self):
         try:
@@ -126,9 +184,14 @@ class Executive:
             tb[i] = fn, ln, nm, line
 
     def interrupt_the_server(self):
-        # XXX KBK 05Feb03 Windows requires this be done with messages and
-        #                 threads....
         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):
         return RemoteDebugger.start_debugger(self.rpchandler, gui_adap_oid)