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 )






_______________________________________________
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 )

Reply via email to