[Zodb-checkins] CVS: ZODB3/ZEO/zrpc - trigger.py:1.3
Jeremy Hylton
jeremy@zope.com
Thu, 22 Aug 2002 15:12:37 -0400
Update of /cvs-repository/ZODB3/ZEO/zrpc
In directory cvs.zope.org:/tmp/cvs-serv26924/zrpc
Modified Files:
trigger.py
Log Message:
There were two copies trigger!
The one in ZEO/trigger.py was the good code, but we want it to live in
ZEO/zrpc/trigger.py. This checkin makes that change.
The key checkin of ZEO/trigger.py had this checkin comment:
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.
=== ZODB3/ZEO/zrpc/trigger.py 1.2 => 1.3 ===
--- ZODB3/ZEO/zrpc/trigger.py:1.2 Tue Jun 11 15:22:26 2002
+++ ZODB3/ZEO/zrpc/trigger.py Thu Aug 22 15:12:37 2002
@@ -11,13 +11,11 @@
# FOR A PARTICULAR PURPOSE
#
##############################################################################
-# This module is a simplified version of the select_trigger module
-# from Sam Rushing's Medusa server.
-
import asyncore
import os
import socket
+import string
import thread
if os.name == 'posix':
@@ -55,16 +53,25 @@
# the main thread is trying to remove some]
def __init__ (self):
- r, w = os.pipe()
+ r, w = self._fds = os.pipe()
self.trigger = w
asyncore.file_dispatcher.__init__ (self, r)
self.lock = thread.allocate_lock()
self.thunks = []
+ self._closed = None
+
+ # 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):
- self.del_channel()
- self.socket.close() # the read side of the pipe
- os.close(self.trigger) # the write side of the pipe
+ 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)
@@ -79,7 +86,6 @@
pass
def pull_trigger (self, thunk=None):
- # print 'PULL_TRIGGER: ', len(self.thunks)
if thunk:
try:
self.lock.acquire()
@@ -96,14 +102,18 @@
try:
thunk()
except:
- (file, fun, line), t, v, tbinfo = asyncore.compact_traceback()
- print 'exception in trigger thunk: (%s:%s %s)' % (t, v, tbinfo)
+ nil, t, v, tbinfo = asyncore.compact_traceback()
+ print ('exception in trigger thunk:'
+ ' (%s:%s %s)' % (t, v, tbinfo))
self.thunks = []
finally:
self.lock.release()
else:
+ # XXX Should define a base class that has the common methods and
+ # then put the platform-specific in a subclass named trigger.
+
# win32-safe version
class trigger (asyncore.dispatcher):
@@ -118,65 +128,66 @@
w.setsockopt(socket.IPPROTO_TCP, 1, 1)
# tricky: get a pair of connected sockets
- host='127.0.0.1'
- port=19999
+ host = '127.0.0.1'
+ port = 19999
while 1:
try:
- self.address=(host, port)
+ self.address = host, port
a.bind(self.address)
break
except:
if port <= 19950:
raise 'Bind Error', 'Cannot bind trigger!'
- port=port - 1
+ port -= 1
- a.listen (1)
- w.setblocking (0)
+ a.listen(1)
+ w.setblocking(0)
try:
- w.connect (self.address)
+ w.connect(self.address)
except:
pass
r, addr = a.accept()
a.close()
- w.setblocking (1)
+ w.setblocking(1)
self.trigger = w
- asyncore.dispatcher.__init__ (self, r)
+ asyncore.dispatcher.__init__(self, r)
self.lock = thread.allocate_lock()
self.thunks = []
self._trigger_connected = 0
- def __repr__ (self):
+ def __repr__(self):
return '<select-trigger (loopback) at %x>' % id(self)
- def readable (self):
+ def readable(self):
return 1
- def writable (self):
+ def writable(self):
return 0
- def handle_connect (self):
+ def handle_connect(self):
pass
- def pull_trigger (self, thunk=None):
+ def pull_trigger(self, thunk=None):
if thunk:
try:
self.lock.acquire()
- self.thunks.append (thunk)
+ self.thunks.append(thunk)
finally:
self.lock.release()
- self.trigger.send ('x')
+ self.trigger.send('x')
- def handle_read (self):
- self.recv (8192)
+ def handle_read(self):
+ self.recv(8192)
try:
self.lock.acquire()
for thunk in self.thunks:
try:
thunk()
except:
- (file, fun, line), t, v, tbinfo = asyncore.compact_traceback()
- print 'exception in trigger thunk: (%s:%s %s)' % (t, v, tbinfo)
+ nil, t, v, tbinfo = asyncore.compact_traceback()
+ print ('exception in trigger thunk:'
+ ' (%s:%s %s)' % (t, v, tbinfo))
self.thunks = []
finally:
self.lock.release()