Tim Peters wrote:
[Tim]
... At this point, I wouldn't consider using it [SO_EXCLUSIVEADDRUSE] unless someone first took the tedious time it needs to demonstrate that when it is used, the thing that _I_ think is a bug here goes away in its presence: the seeming ability of Windows to sometimes permit more than one socket to bind to the same address simultaneously (not serially -- Windows does seem to prevent that reliably).
I started, but didn't get that far. The first time I ran a pair of processes with the attached (Python 2.4.1, WinXP Pro SP2), one fell over with
... w.connect((host, port)) File "<string>", line 1, in connect socket.error: (10048, 'Address already in use')
after about 20 minutes.
So, on the face of it, playing with SO_EXCLUSIVEADDRUSE is no better than the ZODB 3.4 Windows socket dance. Both appear mounds better-behaved than the Medusa Windows socket dance without SO_EXCLUSIVEADDRUSE, though. Since there are fewer other problems associated with the ZODB 3.4 version (see last email), I'd like to repeat this part:
If you can, I would like you to try the ZODB 3.4 Windows socket dance code, and see if it works for you in practice. I know it's not bulletproof, but it's portable across all flavors of Windows and is much better-behaved in my tests so far than the Medusa Windows socket dance.
Bulletproof appears impossible due to what still look like race bugs in the Windows socket implementation.
I agree that SO_EXCLUSIVEADDRUSE is not interesting - I was too focused on what I thougth was an error (See the previous posts ). After your first post I changed select_trigger.py of medusa in my zope installation to work like the ZODB 3.4 code (like the ZODB svn trunk). The problem I had with blocks during accept() is not appearing, and my zopes are running fine. Tests: A) socktest111 on win2k: It runs fine for more than 3 hours, but a few (1 out of 100) exceptions on bind() (a printed x) appears. B) socktest15 on my winxp home sp2 With SO_EXCLUSIVEADDRUSE: Has been running for 4 hours without problems. Needs ports down to around 19800. Without SO_EXCLUSIVEADDRUSE: it fails after very few cycles, (just like socktest111) like this: . . . . Traceback (most recent call last): File "peters2.py", line 72, in ? stuff = socktest15() File "peters2.py", line 33, in socktest15 w.connect((host, port)) File "<string>", line 1, in connect socket.error: (10061, 'Connection refused') (The old problem. I can't reproduce it on other machines.) C) socktest15 on win2k: With SO_EXCLUSIVEADDRUSE: Runs fine for more than 3 hours, needs ports down to around 19800 I did not get an "Address already in use" in that amount of time. Without SO_EXCLUSIVEADDRUSE: Runs fine for more than 3 hours, needs usually only to go down to 19998 I have not yet been able to get access to the machines for a longer period. As said above socktest111 and socktest15 (without SO_EXCLUSIVEADDRUSE) fails immediately on my own machine, so I have not yet been able to reproduce the processes swapping connection (the failing assert). D) socktest2 on my winxp home sp2: Have run it several times. Fails after a varying amount of time (2-15 minuttes) with an (10048, 'Address already in use') in connect. (Just like you experienced). Sometimes both processes fail at the same time, sometimes only one of them. C) socktest29 Will try to run that now, and let run a while.
Here's the code. Note that it changed to try (no more than) 10,000 ports, although I didn't see it need to go through more than 200:
import socket, errno import time, random
class BindError(Exception): pass
def socktest15(): """Like socktest1, but w/o pointless blocking games. Added SO_EXCLUSIVEADDRUSE to the server socket. """
a = socket.socket() w = socket.socket()
a.setsockopt(socket.SOL_SOCKET, socket.SO_EXCLUSIVEADDRUSE, 1) # set TCP_NODELAY to true to avoid buffering w.setsockopt(socket.IPPROTO_TCP, 1, 1) # tricky: get a pair of connected sockets host = '127.0.0.1' port = 19999
while 1: try: a.bind((host, port)) break except: if port <= 10000: raise BindError, 'Cannot bind trigger!' port -= 1
port2count[port] = port2count.get(port, 0) + 1 a.listen(1) w.connect((host, port)) r, addr = a.accept() a.close()
return (r, w)
def close(r, w): for s in r, w: s.close() return # the fancy stuff below didn't help or hurt for s in w, r: s.shutdown(socket.SHUT_WR) for s in w, r: while 1: msg = s.recv(10) if msg == "": break print "eh?!", repr(msg) for s in w, r: s.close()
port2count = {}
def dump(): print items = port2count.items() items.sort() for pair in items: print "%5d %7d" % pair
sofar = [] i = 0 try: while 1: if i % 1000 == 0: dump() i += 1 print '.', try: stuff = socktest15() except RuntimeError: raise sofar.append(stuff) time.sleep(random.random()/10) if len(sofar) == 50: tup = sofar.pop(0) r, w = tup msg = str(random.randrange(1000000)) w.send(msg) msg2 = r.recv(100) assert msg == msg2, (msg, msg2, r.getsockname(), w.getsockname()) close(r, w) except KeyboardInterrupt: for tup in sofar: close(*tup) _______________________________________________ Zope maillist - Zope@zope.org http://mail.zope.org/mailman/listinfo/zope ** No cross posts or HTML encoding! ** (Related lists - http://mail.zope.org/mailman/listinfo/zope-announce http://mail.zope.org/mailman/listinfo/zope-dev )