Repository: ipython/ipython

Issue:
Too many files open when starting and stopping kernel repeatedly
The following code:

```
from IPython.kernel import KernelManager

for i in range(1000):
    km = KernelManager()
    km.start_kernel(extra_arguments=['--pylab=inline'])
    kc = km.client()
    kc.start_channels()
    kc.stop_channels()
    km.shutdown_kernel(now=True)
```

causes the following exception after ~27 iterations:

```
Traceback (most recent call last):
  File "test_ipython.py", line 6, in <module>
  File "/Users/tom/Library/Python/2.7/lib/python/site-packages/IPython/kernel/manager.py", line 202, in start_kernel
  File "/Users/tom/Library/Python/2.7/lib/python/site-packages/IPython/kernel/connect.py", line 481, in write_connection_file
  File "/Users/tom/Library/Python/2.7/lib/python/site-packages/IPython/kernel/connect.py", line 110, in write_connection_file
  File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/socket.py", line 187, in __init__
socket.error: [Errno 24] Too many open files
```

It looks like some file handles are not getting closed? I had a look and didn't see anything obvious. This is relevant for e.g. [runipy](https://github.com/paulgb/runipy) because it starts and stops the kernel each time a different notebook is run.

(also, the error occurs even if I set `now=False`).



Discussion:
I can reproduce. It causes and fatal error in libzmq for me after 21 iterations.


Resolving patch:
diff --git a/IPython/kernel/channels.py b/IPython/kernel/channels.py
--- a/IPython/kernel/channels.py
+++ b/IPython/kernel/channels.py
@@ -137,7 +137,23 @@ def stop(self):
         terminates. :class:`RuntimeError` will be raised if
         :meth:`~threading.Thread.start` is called again.
         """
+        if self.ioloop is not None:
+            self.ioloop.stop()
         self.join()
+        self.close()
+    
+    def close(self):
+        if self.ioloop is not None:
+            try:
+                self.ioloop.close(all_fds=True)
+            except Exception:
+                pass
+        if self.socket is not None:
+            try:
+                self.socket.close(linger=0)
+            except Exception:
+                pass
+            self.socket = None
 
     @property
     def address(self):
@@ -198,15 +214,6 @@ def run(self):
         self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
         self.stream.on_recv(self._handle_recv)
         self._run_loop()
-        try:
-            self.socket.close()
-        except:
-            pass
-
-    def stop(self):
-        """Stop the channel's event loop and join its thread."""
-        self.ioloop.stop()
-        super(ShellChannel, self).stop()
 
     def call_handlers(self, msg):
         """This method is called in the ioloop thread when a message arrives.
@@ -407,15 +414,6 @@ def run(self):
         self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
         self.stream.on_recv(self._handle_recv)
         self._run_loop()
-        try:
-            self.socket.close()
-        except:
-            pass
-
-    def stop(self):
-        """Stop the channel's event loop and join its thread."""
-        self.ioloop.stop()
-        super(IOPubChannel, self).stop()
 
     def call_handlers(self, msg):
         """This method is called in the ioloop thread when a message arrives.
@@ -475,15 +473,6 @@ def run(self):
         self.stream = zmqstream.ZMQStream(self.socket, self.ioloop)
         self.stream.on_recv(self._handle_recv)
         self._run_loop()
-        try:
-            self.socket.close()
-        except:
-            pass
-
-    def stop(self):
-        """Stop the channel's event loop and join its thread."""
-        self.ioloop.stop()
-        super(StdInChannel, self).stop()
 
     def call_handlers(self, msg):
         """This method is called in the ioloop thread when a message arrives.
@@ -603,10 +592,6 @@ def run(self):
                 # and close/reopen the socket, because the REQ/REP cycle has been broken
                 self._create_socket()
                 continue
-        try:
-            self.socket.close()
-        except:
-            pass
 
     def pause(self):
         """Pause the heartbeat."""
diff --git a/IPython/kernel/manager.py b/IPython/kernel/manager.py
--- a/IPython/kernel/manager.py
+++ b/IPython/kernel/manager.py
@@ -240,11 +240,7 @@ def shutdown_kernel(self, now=False, restart=False):
         self.stop_restarter()
 
         # FIXME: Shutdown does not work on Windows due to ZMQ errors!
-        if sys.platform == 'win32':
-            self._kill_kernel()
-            return
-
-        if now:
+        if now or sys.platform == 'win32':
             if self.has_kernel:
                 self._kill_kernel()
         else:
@@ -267,6 +263,8 @@ def shutdown_kernel(self, now=False, restart=False):
             self.cleanup_ipc_files()
         else:
             self.cleanup_ipc_files()
+        
+        self._close_control_socket()
 
     def restart_kernel(self, now=False, **kw):
         """Restarts a kernel with the arguments that were used to launch it.


Test patch: