[Idle-dev] CVS: idle PyShell.py,1.58,1.59 run.py,1.13,1.14

Kurt B. Kaiser kbk@users.sourceforge.net
Sat, 22 Mar 2003 11:40:21 -0800


Update of /cvsroot/idlefork/idle
In directory sc8-pr-cvs1:/tmp/cvs-serv26496

Modified Files:
	PyShell.py run.py 
Log Message:
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.)


Index: PyShell.py
===================================================================
RCS file: /cvsroot/idlefork/idle/PyShell.py,v
retrieving revision 1.58
retrieving revision 1.59
diff -C2 -r1.58 -r1.59
*** PyShell.py	10 Mar 2003 20:42:22 -0000	1.58
--- PyShell.py	22 Mar 2003 19:40:18 -0000	1.59
***************
*** 368,383 ****
                  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.rpcclt.close()
              os.wait()
          except:
              pass
          self.spawn_subprocess()
          self.rpcclt.accept()
--- 368,379 ----
                  pass
          # Kill subprocess, spawn a new one, accept connection.
          try:
+             self.interrupt_subprocess()
+             self.shutdown_subprocess()
              self.rpcclt.close()
              os.wait()
          except:
              pass
+         self.tkconsole.executing = False
          self.spawn_subprocess()
          self.rpcclt.accept()
***************
*** 407,414 ****
  
      def __request_interrupt(self):
!         self.rpcclt.asynccall("exec", "interrupt_the_server", (), {})
  
      def interrupt_subprocess(self):
!         if hasattr(os, "kill"):
              self.__signal_interrupt()
          else:
--- 403,415 ----
  
      def __request_interrupt(self):
!         try:
!             self.rpcclt.asynccall("exec", "interrupt_the_server", (), {})
!         except:
!             pass
  
      def interrupt_subprocess(self):
!         # XXX KBK 22Mar03 Use interrupt message on all platforms for now.
!         # XXX if hasattr(os, "kill"):
!         if False:
              self.__signal_interrupt()
          else:
***************
*** 417,420 ****
--- 418,432 ----
              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:
***************
*** 469,475 ****
      def kill_subprocess(self):
          clt = self.rpcclt
-         self.rpcclt = None
          if clt is not None:
              clt.close()
  
      debugger = None
--- 481,488 ----
      def kill_subprocess(self):
          clt = self.rpcclt
          if clt is not None:
+             self.shutdown_subprocess()
              clt.close()
+         self.rpcclt = None
  
      debugger = None

Index: run.py
===================================================================
RCS file: /cvsroot/idlefork/idle/run.py,v
retrieving revision 1.13
retrieving revision 1.14
diff -C2 -r1.13 -r1.14
*** run.py	11 Mar 2003 22:55:56 -0000	1.13
--- run.py	22 Mar 2003 19:40:19 -0000	1.14
***************
*** 3,6 ****
--- 3,8 ----
  import socket
  import traceback
+ import threading
+ import Queue
  
  import boolcheck
***************
*** 11,17 ****
--- 13,30 ----
  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
***************
*** 21,25 ****
      the mix-in class SocketIO.
  
!     When the RPCServer svr 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
--- 34,38 ----
      the mix-in class SocketIO.
  
!     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
***************
*** 32,44 ****
  
      """
      port = 8833
      if sys.argv[1:]:
          port = int(sys.argv[1])
      sys.argv[:] = [""]
!     addr = ("localhost", port)
      for i in range(6):
          time.sleep(i)
          try:
!             svr = rpc.RPCServer(addr, MyHandler)
              break
          except socket.error, err:
--- 45,77 ----
  
      """
+     global queue, execution_finished, exit_requested
+ 
      port = 8833
      if sys.argv[1:]:
          port = int(sys.argv[1])
      sys.argv[:] = [""]
!     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:
!             server = rpc.RPCServer(address, MyHandler)
              break
          except socket.error, err:
***************
*** 50,59 ****
      else:
          print>>sys.__stderr__, "\nConnection to Idle failed, exiting."
!         sys.exit()
!     svr.handle_request() # A single request only
  
  class MyHandler(rpc.RPCHandler):
  
      def handle(self):
          executive = Executive(self)
          self.register("exec", executive)
--- 83,94 ----
      else:
          print>>sys.__stderr__, "\nConnection to Idle failed, exiting."
!         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)
***************
*** 61,65 ****
          sys.stdout = self.get_remote_proxy("stdout")
          sys.stderr = self.get_remote_proxy("stderr")
!         rpc.RPCHandler.handle(self)
  
  class Executive:
--- 96,101 ----
          sys.stdout = self.get_remote_proxy("stdout")
          sys.stderr = self.get_remote_proxy("stderr")
!         rpc.RPCHandler.getresponse(self, myseq=None, wait=0.5)
! 
  
  class Executive:
***************
*** 71,74 ****
--- 107,129 ----
  
      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
***************
*** 86,90 ****
              for line in lines:
                  print>>efile, line,
!         self.flush_stdout()
  
      def flush_stdout(self):
--- 141,148 ----
              for line in lines:
                  print>>efile, line,
!             execution_finished = True
!         else:
!             self.flush_stdout()
!             execution_finished = True
  
      def flush_stdout(self):
***************
*** 127,133 ****
  
      def interrupt_the_server(self):
-         # XXX KBK 05Feb03 Windows requires this be done with messages and
-         #                 threads....
          self.rpchandler.interrupted = True
  
      def start_the_debugger(self, gui_adap_oid):
--- 185,196 ----
  
      def interrupt_the_server(self):
          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):