[Zodb-checkins] CVS: StandaloneZODB/ZEO - trigger.py:1.3.4.2

Jeremy Hylton jeremy@zope.com
Tue, 15 Jan 2002 12:22:11 -0500


Update of /cvs-repository/StandaloneZODB/ZEO
In directory cvs.zope.org:/tmp/cvs-serv473

Modified Files:
      Tag: Standby-branch
	trigger.py 
Log Message:
Fix trigger close/__del__.

The close() mechanism for an asyncore file_dispatcher is not safe to
call multiple times.  It's calling os.close() on a file descriptor
(int).  Guido observed that if you call close() twice, you could be in
trouble:  1) First close() call closes FD 6.  2) Another bit of code
opens a new file, getting FD 6.  3) Second close() call closes FD 6.
Waah!  FD 6 is some other file.

The workaround attempt here is to define a close() method on a trigger
that only closes the file descriptors the first time.

Also, make sure that both file descriptors are closed.  The previous
version only closed the read-end of the pipe.


=== StandaloneZODB/ZEO/trigger.py 1.3.4.1 => 1.3.4.2 ===
 # INFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE.
 
-
 # This module is a simplified version of the select_trigger module
 # from Sam Rushing's Medusa server.
 
-
 import asyncore
-#import asynchat
 
 import os
 import socket
@@ -60,10 +57,20 @@
             asyncore.file_dispatcher.__init__ (self, r)
             self.lock = thread.allocate_lock()
             self.thunks = []
+            self._closed = None
 
-        def __del__(self):
-            os.close(self._fds[0])
-            os.close(self._fds[1])
+        # Override the asyncore close() method, because it seems that
+        # it would only close the r file descriptor and not w.  The
+        # constructor calls file_dispactcher.__init__ and passes r,
+        # which would get stored in a file_wrapper and get closed by
+        # the default close.  But that would leave w open...
+
+        def close(self):
+            if self._closed is None:
+                self._closed = 1
+                self.del_channel()
+                for fd in self._fds:
+                    os.close(fd)
 
         def __repr__ (self):
             return '<select-trigger (pipe) at %x>' % id(self)
@@ -78,7 +85,6 @@
             pass
 
         def pull_trigger (self, thunk=None):
-            # print 'PULL_TRIGGER: ', len(self.thunks)
             if thunk:
                 try:
                     self.lock.acquire()