[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[minion-cvs] Fix a very nasty deadlock bug.



Update of /home/minion/cvsroot/src/minion/lib/mixminion
In directory moria.seul.org:/tmp/cvs-serv8779/lib/mixminion

Modified Files:
	MMTPServer.py 
Log Message:
Fix a very nasty deadlock bug.

MMTPServer:
	- Reimplement __shutdownFn to solve a nasty deadlock bug when we try
	  to send a message to ourself.  See comments for more information.
	- Improve debugging output
	- Use new 'stringContains' function
	- Add hasReader/hasWriter to AsyncServer


Index: MMTPServer.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/MMTPServer.py,v
retrieving revision 1.17
retrieving revision 1.18
diff -u -d -r1.17 -r1.18
--- MMTPServer.py	10 Sep 2002 14:45:30 -0000	1.17
+++ MMTPServer.py	2 Dec 2002 03:21:12 -0000	1.18
@@ -29,7 +29,7 @@
 from types import StringType
 
 import mixminion._minionlib as _ml
-from mixminion.Common import MixError, MixFatalError, getLog
+from mixminion.Common import MixError, MixFatalError, getLog, stringContains
 from mixminion.Crypto import sha1
 from mixminion.Packet import MESSAGE_LEN, DIGEST_LEN
 
@@ -63,8 +63,10 @@
            If we receive an unblocked signal, return immediately.
            """
 
-##        trace("%s readers, %s writers" % (len(self.readers),
-##                                          len(self.writers)))
+##         trace("%s readers (%s), %s writers (%s)" % (len(self.readers),
+## 						    readers,
+## 						    len(self.writers),
+## 						    writers))
         
         readfds = self.readers.keys()
         writefds = self.writers.keys()
@@ -77,16 +79,26 @@
                 raise e
 
         for fd in readfds:
-            trace("Got a read on "+str(fd))
+            trace("Select got a read on "+str(fd))
             self.readers[fd].handleRead()
         for fd in writefds:
-            trace("Got a write on"+str(fd))
+            trace("Select got a write on"+str(fd))
             self.writers[fd].handleWrite()
         for fd in exfds:
-            trace("Got an exception on"+str(fd))
+            trace("Select got an exception on"+str(fd))
             if self.readers.has_key(fd): del self.readers[fd]
             if self.writers.has_key(fd): del self.writers[fd]
 
+    def hasReader(self, reader):
+	"""Return true iff 'reader' is a reader on this server."""
+	fd = reader.fileno()
+	return self.readers.get(fd, None) is reader
+
+    def hasWriter(self, writer):
+	"""Return true iff 'writer' is a writer on this server."""
+	fd = writer.fileno()
+	return self.writers.get(fd, None) is writer
+
     def registerReader(self, reader):
         """Register a connection as a reader.  The connection's 'handleRead'
            method will be called whenever data is available for reading."""
@@ -271,15 +283,27 @@
 
     def __shutdownFn(self):
         """Hook to implement shutdown."""
-        r = self.__con.shutdown() #may throw want*
-        if r == 1:
-            trace("Got a 1 on shutdown")
-            self.__server.unregister(self)
-            self.__state = None
-            self.__sock.close()
-            self.shutdownFinished()
-        else:
-            trace("Got a 0 on shutdown")
+
+	# This is a bit subtle.  The underlying 'shutdown' method
+	# needs to be retried till the other guy sends an 'ack'
+	# back... but we don't want to keep retrying indefinitely, or
+	# else we can deadlock on a connection from ourself to
+	# ourself.
+	if self.__con.shutdown() == 1: #may throw want*
+	    trace("Got a 1 on shutdown")
+	    self.__server.unregister(self)
+	    self.__state = None
+	    self.__sock.close()
+	    self.shutdownFinished()
+	    return
+
+	# If we don't get any response on shutdown, stop blocking; the other
+	# side may be hostile, confused, or deadlocking.
+	trace("Got a 0 on shutdown")
+	# ???? Is 'wantread' always correct?
+	# ???? Rather than waiting for a read, should we use a timer or 
+	# ????    something?
+	raise _ml.TLSWantRead()
 
     def __readFn(self):
         """Hook to implement read"""
@@ -305,7 +329,8 @@
             self.shutdown(err=1, retriable=0)
             return
          
-        if self.__terminator and self.__inbuf[0].find(self.__terminator) > -1:
+        if self.__terminator and stringContains(self.__inbuf[0], 
+						self.__terminator):
             trace("read found terminator")
             self.__server.unregister(self)
             self.finished()
@@ -346,7 +371,7 @@
             # We have a while loop here so that, upon entering a new
             # state, we immediately see if we can go anywhere with it
             # without blocking.
-            while self.__state is not None:
+	    while self.__state is not None:
                 self.__state()
         except _ml.TLSWantWrite:
             self.__server.registerWriter(self)
@@ -649,7 +674,7 @@
 
     def sendMessages(self, ip, port, keyID, messages, handles):
 	"""Send a set of messages to a given server."""
-	#XXXX for debugging
+	# ???? Can we remove these asserts yet?
 	for m,h in zip(messages, handles):
 	    assert len(m) == MESSAGE_LEN
 	    assert len(h) < 32