1. Implement processing of user code in subprocess MainThread.  Pass loop
   is now interruptable on Windows.
2. Tweak signal.signal() wait parameters as called by various methods
   to improve I/O response, especially on Windows.
3. Debugger is disabled at this check-in pending further development.

M NEWS.txt
M PyShell.py
M rpc.py
M run.py
diff --git a/Lib/idlelib/NEWS.txt b/Lib/idlelib/NEWS.txt
index 2ee9776..94c0d12 100644
--- a/Lib/idlelib/NEWS.txt
+++ b/Lib/idlelib/NEWS.txt
@@ -5,7 +5,13 @@
 What's New in IDLEfork 0.9b1?
 ===================================
 
-*Release date: 25-Apr-2003*
+*Release date: XX-XXX-2003*
+
+- Improved I/O response by tweaking the wait parameter in various
+  calls to signal.signal().
+
+- Implemented a threaded subprocess which allows interrupting a pass 
+  loop in user code using the 'interrupt' extension.
 
 - Implemented the 'interrupt' extension module, which allows a subthread
   to raise a KeyboardInterrupt in the main thread.
@@ -36,11 +42,10 @@
 
 - Known issues:
 
-  + Can't kill/restart a tight loop in the Windows version: add 
-    I/O to the loop or use the Task Manager to kill the subprocess.
   + Typing two Control-C in close succession when the subprocess is busy can
     cause IDLE to lose communication with the subprocess.  Please type one
-    only and wait for the exception to complete.
+    only and wait for the exception to complete.  If you do manage to 
+    interrupt the interrupt, simply restart the shell.
   + Printing under some versions of Linux may be problematic.
 
 
diff --git a/Lib/idlelib/PyShell.py b/Lib/idlelib/PyShell.py
index 9f158f5..483a921 100644
--- a/Lib/idlelib/PyShell.py
+++ b/Lib/idlelib/PyShell.py
@@ -35,6 +35,11 @@
 
 IDENTCHARS = string.ascii_letters + string.digits + "_"
 
+try:
+    from signal import SIGTERM
+except ImportError:
+    SIGTERM = 15
+
 # Change warnings module to write to sys.__stderr__
 try:
     import warnings
@@ -367,13 +372,8 @@
             except:
                 pass
         # Kill subprocess, spawn a new one, accept connection.
-        try:
-            self.interrupt_subprocess()
-            self.shutdown_subprocess()
-            self.rpcclt.close()
-            os.wait()
-        except:
-            pass
+        self.rpcclt.close()
+        self.unix_terminate()
         self.tkconsole.executing = False
         self.spawn_subprocess()
         self.rpcclt.accept()
@@ -391,42 +391,31 @@
             # reload remote debugger breakpoints for all PyShellEditWindows
             debug.load_breakpoints()
 
-    def __signal_interrupt(self):
-        try:
-            from signal import SIGINT
-        except ImportError:
-            SIGINT = 2
-        try:
-            os.kill(self.rpcpid, SIGINT)
-        except OSError:    # subprocess may have already exited
-            pass
-
     def __request_interrupt(self):
-        try:
-            self.rpcclt.asynccall("exec", "interrupt_the_server", (), {})
-        except:
-            pass
+        self.rpcclt.remotecall("exec", "interrupt_the_server", (), {})
 
     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:
-            # 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()
+        threading.Thread(target=self.__request_interrupt).start()
 
-    def __request_shutdown(self):
-        try:
-            self.rpcclt.asynccall("exec", "shutdown_the_server", (), {})
-        except:
-            pass
+    def kill_subprocess(self):
+        self.rpcclt.close()
+        self.unix_terminate()
+        self.tkconsole.executing = False
+        self.rpcclt = None
 
-    def shutdown_subprocess(self):
-        t = threading.Thread(target=self.__request_shutdown)
-        t.start()
-        t.join()
+    def unix_terminate(self):
+        "UNIX: make sure subprocess is terminated and collect status"
+        if hasattr(os, 'kill'):
+            try:
+                os.kill(self.rpcpid, SIGTERM)
+            except OSError:
+                # process already terminated:
+                return
+            else:
+                try:
+                    os.waitpid(self.rpcpid, 0)
+                except OSError:
+                    return
 
     def transfer_path(self):
         self.runcommand("""if 1:
@@ -445,21 +434,15 @@
         if clt is None:
             return
         try:
-            response = clt.pollresponse(self.active_seq)
-        except (EOFError, IOError):
-            # lost connection: subprocess terminated itself, restart
+            response = clt.pollresponse(self.active_seq, wait=0.05)
+        except (EOFError, IOError, KeyboardInterrupt):
+            # lost connection or subprocess terminated itself, restart
+            # [the KBI is from rpc.SocketIO.handle_EOF()]
             if self.tkconsole.closing:
                 return
             response = None
-            try:
-                # stake any zombie before restarting
-                os.wait()
-            except (AttributeError, OSError):
-                pass
             self.restart_subprocess()
             self.tkconsole.endexecuting()
-        # Reschedule myself in 50 ms
-        self.tkconsole.text.after(50, self.poll_subprocess)
         if response:
             self.tkconsole.resetoutput()
             self.active_seq = None
@@ -477,13 +460,8 @@
                 print >>console, errmsg, what
             # we received a response to the currently active seq number:
             self.tkconsole.endexecuting()
-
-    def kill_subprocess(self):
-        clt = self.rpcclt
-        if clt is not None:
-            self.shutdown_subprocess()
-            clt.close()
-        self.rpcclt = None
+        # Reschedule myself in 50 ms
+        self.tkconsole.text.after(50, self.poll_subprocess)
 
     debugger = None
 
@@ -495,7 +473,7 @@
 
     def remote_stack_viewer(self):
         import RemoteObjectBrowser
-        oid = self.rpcclt.remotecall("exec", "stackviewer", ("flist",), {})
+        oid = self.rpcclt.remotequeue("exec", "stackviewer", ("flist",), {})
         if oid is None:
             self.tkconsole.root.bell()
             return
@@ -628,7 +606,7 @@
             self.display_executing_dialog()
             return 0
         if self.rpcclt:
-            self.rpcclt.remotecall("exec", "runcode", (code,), {})
+            self.rpcclt.remotequeue("exec", "runcode", (code,), {})
         else:
             exec code in self.locals
         return 1
@@ -645,7 +623,7 @@
         self.tkconsole.beginexecuting()
         try:
             if not debugger and self.rpcclt is not None:
-                self.active_seq = self.rpcclt.asynccall("exec", "runcode",
+                self.active_seq = self.rpcclt.asyncqueue("exec", "runcode",
                                                         (code,), {})
             elif debugger:
                 debugger.run(code, self.locals)
@@ -712,7 +690,7 @@
         text.bind("<<beginning-of-line>>", self.home_callback)
         text.bind("<<end-of-file>>", self.eof_callback)
         text.bind("<<open-stack-viewer>>", self.open_stack_viewer)
-        text.bind("<<toggle-debugger>>", self.toggle_debugger)
+        ##text.bind("<<toggle-debugger>>", self.toggle_debugger)
         text.bind("<<open-python-shell>>", self.flist.open_shell)
         text.bind("<<toggle-jit-stack-viewer>>", self.toggle_jit_stack_viewer)
         text.bind("<<view-restart>>", self.view_restart_mark)
@@ -799,13 +777,9 @@
         "Helper for ModifiedInterpreter"
         self.resetoutput()
         self.executing = 1
-        ##self._cancel_check = self.cancel_check
-        ##sys.settrace(self._cancel_check)
 
     def endexecuting(self):
         "Helper for ModifiedInterpreter"
-        ##sys.settrace(None)
-        ##self._cancel_check = None
         self.executing = 0
         self.canceled = 0
         self.showprompt()
@@ -822,7 +796,6 @@
                 return "cancel"
             # interrupt the subprocess
             self.closing = True
-            self.cancel_callback()
             self.endexecuting()
         return EditorWindow.close(self)
 
@@ -1017,23 +990,6 @@
         line = line[:i]
         more = self.interp.runsource(line)
 
-    def cancel_check(self, frame, what, args,
-                     dooneevent=tkinter.dooneevent,
-                     dontwait=tkinter.DONT_WAIT):
-        # Hack -- use the debugger hooks to be able to handle events
-        # and interrupt execution at any time.
-        # This slows execution down quite a bit, so you may want to
-        # disable this (by not calling settrace() in beginexecuting() and
-        # endexecuting() for full-bore (uninterruptable) speed.)
-        # XXX This should become a user option.
-        if self.canceled:
-            return
-        dooneevent(dontwait)
-        if self.canceled:
-            self.canceled = 0
-            raise KeyboardInterrupt
-        return self._cancel_check
-
     def open_stack_viewer(self, event=None):
         if self.interp.rpcclt:
             return self.interp.remote_stack_viewer()
diff --git a/Lib/idlelib/rpc.py b/Lib/idlelib/rpc.py
index 15946a6..4c3ef3e 100644
--- a/Lib/idlelib/rpc.py
+++ b/Lib/idlelib/rpc.py
@@ -28,17 +28,21 @@
 """
 
 import sys
+import os
 import socket
 import select
 import SocketServer
 import struct
 import cPickle as pickle
 import threading
+import Queue
 import traceback
 import copy_reg
 import types
 import marshal
 
+import interrupt
+
 def unpickle_code(ms):
     co = marshal.loads(ms)
     assert isinstance(co, types.CodeType)
@@ -98,8 +102,6 @@
             raise
         except SystemExit:
             raise
-        except EOFError:
-            pass
         except:
             erf = sys.__stderr__
             print>>erf, '\n' + '-'*40
@@ -110,28 +112,29 @@
             traceback.print_exc(file=erf)
             print>>erf, '\n*** Unrecoverable, server exiting!'
             print>>erf, '-'*40
-            import os
             os._exit(0)
 
+#----------------- end class RPCServer --------------------
 
 objecttable = {}
+request_queue = Queue.Queue(0)
+response_queue = Queue.Queue(0)
+
 
 class SocketIO:
 
     nextseq = 0
 
     def __init__(self, sock, objtable=None, debugging=None):
-        self.mainthread = threading.currentThread()
+        self.sockthread = threading.currentThread()
         if debugging is not None:
             self.debugging = debugging
         self.sock = sock
         if objtable is None:
             objtable = objecttable
         self.objtable = objtable
-        self.cvar = threading.Condition()
         self.responses = {}
         self.cvars = {}
-        self.interrupted = False
 
     def close(self):
         sock = self.sock
@@ -139,6 +142,10 @@
         if sock is not None:
             sock.close()
 
+    def exithook(self):
+        "override for specific exit action"
+        os._exit()
+
     def debug(self, *args):
         if not self.debugging:
             return
@@ -156,13 +163,12 @@
         except KeyError:
             pass
 
-    def localcall(self, request):
+    def localcall(self, seq, request):
         self.debug("localcall:", request)
         try:
             how, (oid, methodname, args, kwargs) = request
         except TypeError:
             return ("ERROR", "Bad request format")
-        assert how == "call"
         if not self.objtable.has_key(oid):
             return ("ERROR", "Unknown object id: %s" % `oid`)
         obj = self.objtable[oid]
@@ -178,14 +184,20 @@
             return ("ERROR", "Unsupported method name: %s" % `methodname`)
         method = getattr(obj, methodname)
         try:
-            ret = method(*args, **kwargs)
-            if isinstance(ret, RemoteObject):
-                ret = remoteref(ret)
-            return ("OK", ret)
+            if how == 'CALL':
+                ret = method(*args, **kwargs)
+                if isinstance(ret, RemoteObject):
+                    ret = remoteref(ret)
+                return ("OK", ret)
+            elif how == 'QUEUE':
+                request_queue.put((seq, (method, args, kwargs)))
+                return("QUEUED", None)
+            else:
+                return ("ERROR", "Unsupported message type: %s" % how)
         except SystemExit:
             raise
         except socket.error:
-            pass
+            raise
         except:
             self.debug("localcall:EXCEPTION")
             traceback.print_exc(file=sys.__stderr__)
@@ -193,24 +205,37 @@
 
     def remotecall(self, oid, methodname, args, kwargs):
         self.debug("remotecall:asynccall: ", oid, methodname)
-        # XXX KBK 06Feb03 self.interrupted logic may not be necessary if
-        #                 subprocess is threaded.
-        if self.interrupted:
-            self.interrupted = False
-            raise KeyboardInterrupt
         seq = self.asynccall(oid, methodname, args, kwargs)
         return self.asyncreturn(seq)
 
+    def remotequeue(self, oid, methodname, args, kwargs):
+        self.debug("remotequeue:asyncqueue: ", oid, methodname)
+        seq = self.asyncqueue(oid, methodname, args, kwargs)
+        return self.asyncreturn(seq)
+
     def asynccall(self, oid, methodname, args, kwargs):
-        request = ("call", (oid, methodname, args, kwargs))
+        request = ("CALL", (oid, methodname, args, kwargs))
         seq = self.newseq()
+        if threading.currentThread() != self.sockthread:
+            cvar = threading.Condition()
+            self.cvars[seq] = cvar
         self.debug(("asynccall:%d:" % seq), oid, methodname, args, kwargs)
         self.putmessage((seq, request))
         return seq
 
+    def asyncqueue(self, oid, methodname, args, kwargs):
+        request = ("QUEUE", (oid, methodname, args, kwargs))
+        seq = self.newseq()
+        if threading.currentThread() != self.sockthread:
+            cvar = threading.Condition()
+            self.cvars[seq] = cvar
+        self.debug(("asyncqueue:%d:" % seq), oid, methodname, args, kwargs)
+        self.putmessage((seq, request))
+        return seq
+
     def asyncreturn(self, seq):
         self.debug("asyncreturn:%d:call getresponse(): " % seq)
-        response = self.getresponse(seq, wait=None)
+        response = self.getresponse(seq, wait=0.05)
         self.debug(("asyncreturn:%d:response: " % seq), response)
         return self.decoderesponse(response)
 
@@ -218,25 +243,36 @@
         how, what = response
         if how == "OK":
             return what
+        if how == "QUEUED":
+            return None
         if how == "EXCEPTION":
             self.debug("decoderesponse: EXCEPTION")
             return None
+        if how == "EOF":
+            self.debug("decoderesponse: EOF")
+            self.decode_interrupthook()
+            return None
         if how == "ERROR":
             self.debug("decoderesponse: Internal ERROR:", what)
             raise RuntimeError, what
         raise SystemError, (how, what)
 
+    def decode_interrupthook(self):
+        ""
+        raise EOFError
+
     def mainloop(self):
         """Listen on socket until I/O not ready or EOF
 
-        Main thread pollresponse() will loop looking for seq number None, which
+        pollresponse() will loop looking for seq number None, which
         never comes, and exit on EOFError.
 
         """
         try:
-            self.getresponse(myseq=None, wait=None)
+            self.getresponse(myseq=None, wait=0.05)
         except EOFError:
-            pass
+            self.debug("mainloop:return")
+            return
 
     def getresponse(self, myseq, wait):
         response = self._getresponse(myseq, wait)
@@ -256,23 +292,24 @@
 
     def _getresponse(self, myseq, wait):
         self.debug("_getresponse:myseq:", myseq)
-        if threading.currentThread() is self.mainthread:
-            # Main thread: does all reading of requests or responses
-            # Loop here, blocking each time until socket is ready.
+        if threading.currentThread() is self.sockthread:
+            # this thread does all reading of requests or responses
             while 1:
                 response = self.pollresponse(myseq, wait)
                 if response is not None:
                     return response
         else:
-            # Auxiliary thread: wait for notification from main thread
-            self.cvar.acquire()
-            self.cvars[myseq] = self.cvar
+            # wait for notification from socket handling thread
+            cvar = self.cvars[myseq]
+            cvar.acquire()
             while not self.responses.has_key(myseq):
-                self.cvar.wait()
+                cvar.wait()
             response = self.responses[myseq]
+            self.debug("_getresponse:%s: thread woke up: response: %s" %
+                       (myseq, response))
             del self.responses[myseq]
             del self.cvars[myseq]
-            self.cvar.release()
+            cvar.release()
             return response
 
     def newseq(self):
@@ -283,7 +320,7 @@
         self.debug("putmessage:%d:" % message[0])
         try:
             s = pickle.dumps(message)
-        except:
+        except pickle.UnpicklingError:
             print >>sys.__stderr__, "Cannot pickle:", `message`
             raise
         s = struct.pack("<i", len(s)) + s
@@ -293,10 +330,13 @@
             except AttributeError:
                 # socket was closed
                 raise IOError
+            except socket.error:
+                self.debug("putmessage:socketerror:pid:%s" % os.getpid())
+                os._exit(0)
             else:
                 s = s[n:]
 
-    def ioready(self, wait=0.0):
+    def ioready(self, wait):
         r, w, x = select.select([self.sock.fileno()], [], [], wait)
         return len(r)
 
@@ -304,7 +344,7 @@
     bufneed = 4
     bufstate = 0 # meaning: 0 => reading count; 1 => reading data
 
-    def pollpacket(self, wait=0.0):
+    def pollpacket(self, wait):
         self._stage0()
         if len(self.buffer) < self.bufneed:
             if not self.ioready(wait):
@@ -334,7 +374,7 @@
             self.bufstate = 0
             return packet
 
-    def pollmessage(self, wait=0.0):
+    def pollmessage(self, wait):
         packet = self.pollpacket(wait)
         if packet is None:
             return None
@@ -348,45 +388,97 @@
             raise
         return message
 
-    def pollresponse(self, myseq, wait=0.0):
+    def pollresponse(self, myseq, wait):
         """Handle messages received on the socket.
 
-        Some messages received may be asynchronous 'call' commands, and
-        some may be responses intended for other threads.
+        Some messages received may be asynchronous 'call' or 'queue' requests,
+        and some may be responses for other threads.
 
-        Loop until message with myseq sequence number is received.  Save others
-        in self.responses and notify the owning thread, except that 'call'
-        commands are handed off to localcall() and the response sent back
-        across the link with the appropriate sequence number.
+        'call' requests are passed to self.localcall() with the expectation of
+        immediate execution, during which time the socket is not serviced.
+
+        'queue' requests are used for tasks (which may block or hang) to be
+        processed in a different thread.  These requests are fed into
+        request_queue by self.localcall().  Responses to queued requests are
+        taken from response_queue and sent across the link with the associated
+        sequence numbers.  Messages in the queues are (sequence_number,
+        request/response) tuples and code using this module removing messages
+        from the request_queue is responsible for returning the correct
+        sequence number in the response_queue.
+
+        pollresponse() will loop until a response message with the myseq
+        sequence number is received, and will save other responses in
+        self.responses and notify the owning thread.
 
         """
         while 1:
-            message = self.pollmessage(wait)
-            if message is None:  # socket not ready
+            # send queued response if there is one available
+            try:
+                qmsg = response_queue.get(0)
+            except Queue.Empty:
+                pass
+            else:
+                seq, response = qmsg
+                message = (seq, ('OK', response))
+                self.putmessage(message)
+            # poll for message on link
+            try:
+                message = self.pollmessage(wait)
+                if message is None:  # socket not ready
+                    return None
+            except EOFError:
+                self.handle_EOF()
                 return None
-            #wait = 0.0  # poll on subsequent passes instead of blocking
+            except AttributeError:
+                return None
             seq, resq = message
+            how = resq[0]
             self.debug("pollresponse:%d:myseq:%s" % (seq, myseq))
-            if resq[0] == "call":
+            # process or queue a request
+            if how in ("CALL", "QUEUE"):
                 self.debug("pollresponse:%d:localcall:call:" % seq)
-                response = self.localcall(resq)
+                response = self.localcall(seq, resq)
                 self.debug("pollresponse:%d:localcall:response:%s"
                            % (seq, response))
-                self.putmessage((seq, response))
+                if how == "CALL":
+                    self.putmessage((seq, response))
+                elif how == "QUEUE":
+                    # don't acknowledge the 'queue' request!
+                    pass
                 continue
+            # return if completed message transaction
             elif seq == myseq:
                 return resq
+            # must be a response for a different thread:
             else:
-                self.cvar.acquire()
-                cv = self.cvars.get(seq)
+                cv = self.cvars.get(seq, None)
                 # response involving unknown sequence number is discarded,
-                # probably intended for prior incarnation
+                # probably intended for prior incarnation of server
                 if cv is not None:
+                    cv.acquire()
                     self.responses[seq] = resq
                     cv.notify()
-                self.cvar.release()
+                    cv.release()
                 continue
 
+    def handle_EOF(self):
+        "action taken upon link being closed by peer"
+        self.EOFhook()
+        self.debug("handle_EOF")
+        for key in self.cvars:
+            cv = self.cvars[key]
+            cv.acquire()
+            self.responses[key] = ('EOF', None)
+            cv.notify()
+            cv.release()
+        interrupt.interrupt_main()
+        # call our (possibly overridden) exit function
+        self.exithook()
+
+    def EOFhook(self):
+        "Classes using rpc client/server can override to augment EOF action"
+        pass
+
 #----------------- end class SocketIO --------------------
 
 class RemoteObject:
@@ -465,7 +557,8 @@
             self.__getattributes()
         if not self.__attributes.has_key(name):
             raise AttributeError, name
-    __getattr__.DebuggerStepThrough=1
+
+    __getattr__.DebuggerStepThrough = 1
 
     def __getattributes(self):
         self.__attributes = self.sockio.remotecall(self.oid,
diff --git a/Lib/idlelib/run.py b/Lib/idlelib/run.py
index 497cbbd..320525c 100644
--- a/Lib/idlelib/run.py
+++ b/Lib/idlelib/run.py
@@ -1,4 +1,5 @@
 import sys
+import os
 import time
 import socket
 import traceback
@@ -20,12 +21,8 @@
 # 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
 
@@ -44,8 +41,6 @@
     register and unregister themselves.
 
     """
-    global queue, execution_finished, exit_requested
-
     port = 8833
     if sys.argv[1:]:
         port = int(sys.argv[1])
@@ -58,21 +53,23 @@
     while 1:
         try:
             if exit_requested:
-                sys.exit()
-            # XXX KBK 22Mar03 eventually check queue here!
-            pass
-            time.sleep(0.05)
+                os._exit(0)
+            try:
+                seq, request = rpc.request_queue.get(0)
+            except Queue.Empty:
+                time.sleep(0.05)
+                continue
+            method, args, kwargs = request
+            ret = method(*args, **kwargs)
+            rpc.response_queue.put((seq, ret))
         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)
+            server = MyRPCServer(address, MyHandler)
             break
         except socket.error, err:
             if i < 3:
@@ -82,10 +79,41 @@
                                               + err[1] + ", retrying...."
     else:
         print>>sys.__stderr__, "\nConnection to Idle failed, exiting."
+        global exit_requested
         exit_requested = True
+        return
     server.handle_request() # A single request only
 
 
+class MyRPCServer(rpc.RPCServer):
+
+    def handle_error(self, request, client_address):
+        """Override RPCServer method for IDLE
+
+        Interrupt the MainThread and exit server if link is dropped.
+
+        """
+        try:
+            raise
+        except SystemExit:
+            raise
+        except EOFError:
+            global exit_requested
+            exit_requested = True
+            interrupt.interrupt_main()
+        except:
+            erf = sys.__stderr__
+            print>>erf, '\n' + '-'*40
+            print>>erf, 'Unhandled server exception!'
+            print>>erf, 'Thread: %s' % threading.currentThread().getName()
+            print>>erf, 'Client Address: ', client_address
+            print>>erf, 'Request: ', repr(request)
+            traceback.print_exc(file=erf)
+            print>>erf, '\n*** Unrecoverable, server exiting!'
+            print>>erf, '-'*40
+            os._exit(0)
+
+
 class MyHandler(rpc.RPCHandler):
 
     def handle(self):
@@ -95,7 +123,20 @@
         sys.stdin = self.get_remote_proxy("stdin")
         sys.stdout = self.get_remote_proxy("stdout")
         sys.stderr = self.get_remote_proxy("stderr")
-        rpc.RPCHandler.getresponse(self, myseq=None, wait=0.5)
+        rpc.RPCHandler.getresponse(self, myseq=None, wait=0.05)
+
+    def exithook(self):
+        "override SocketIO method - wait for MainThread to shut us down"
+        while 1: pass
+
+    def EOFhook(self):
+        "Override SocketIO method - terminate wait on callback and exit thread"
+        global exit_requested
+        exit_requested = True
+
+    def decode_interrupthook(self):
+        "interrupt awakened thread"
+        interrupt.interrupt_main()
 
 
 class Executive:
@@ -106,44 +147,30 @@
         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:
-            self.flush_stdout()
-            efile = sys.stderr
-            typ, val, tb = info = sys.exc_info()
-            sys.last_type, sys.last_value, sys.last_traceback = info
-            tbe = traceback.extract_tb(tb)
-            print >>efile, 'Traceback (most recent call last):'
-            exclude = ("run.py", "rpc.py", "RemoteDebugger.py", "bdb.py")
-            self.cleanup_traceback(tbe, exclude)
-            traceback.print_list(tbe, file=efile)
-            lines = traceback.format_exception_only(typ, val)
-            for line in lines:
-                print>>efile, line,
-            execution_finished = True
+            try:
+                if exit_requested:
+                    os._exit(0)
+                self.flush_stdout()
+                efile = sys.stderr
+                typ, val, tb = info = sys.exc_info()
+                sys.last_type, sys.last_value, sys.last_traceback = info
+                tbe = traceback.extract_tb(tb)
+                print >>efile, 'Traceback (most recent call last):'
+                exclude = ("run.py", "rpc.py", "threading.py",
+                           "RemoteDebugger.py", "bdb.py")
+                self.cleanup_traceback(tbe, exclude)
+                traceback.print_list(tbe, file=efile)
+                lines = traceback.format_exception_only(typ, val)
+                for line in lines:
+                    print>>efile, line,
+            except:
+                sys.stderr = sys.__stderr__
+                raise
         else:
             self.flush_stdout()
-            execution_finished = True
 
     def flush_stdout(self):
         try:
@@ -184,15 +211,8 @@
             tb[i] = fn, ln, nm, line
 
     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):
         return RemoteDebugger.start_debugger(self.rpchandler, gui_adap_oid)