[Zope] Re: Running more than one instance on windows often block
each other
Sune B. Woeller
sune at syntetisk.dk
Tue Jul 26 07:23:04 EDT 2005
Tim Peters wrote:
> [Sune B. Woeller]
>
>>>...
>>>I can see (with the excellent (and free) 'Process
>>>Explorer' from sysinternals) that the python
>>>processes always opens port 19999, and connects by
>>>that port to themselves on another port (for
>>>instance 2550).
>
>
> [Dieter Maurer]
>
>>You can find the relevant code in
>>"ZServer.medusa.thread.select_trigger.trigger.__init__"
>>
>>In principle, the code should try all sockets between
>>"19999" down to "19950" and fail only when none of them
>>could be bound to...
>
Thanks for the pointer. I have been debugging
select_trigger.py, and has some more info:
The problem is that the call a.accept() sometimes hangs.
Apparently a.bind(self.address) allows us to bind to
a port that another zope instance already is bound to.
The code creates the server socket a, and the client socket w,
and gets the client socket r by connecting w to a. Then it closes a.
a goes out of scope when __init__ terminates, and is probably garbage collected
at some point.
I tried moving the code to the following standalone script, and I can reproduce
the error with that. In the original code w is kept as an instance variable, and
r is passed to asyncore.dispatcher.__init__ and probably kept there. I simulate
that by returning them, then the caller of socktest can keep them around.
I try to call socktest from different processes A and B (two pythons):
(w,r = socktest())
The call in A gets port 19999. The second call, in B, either blocks, or takes
over port 19999 (I see the second process taking over the port in a port scanner.)
a.bind in B does not raise socket.error: (10048, 'Address already in use') as
expected, when the server socket in A is closed, even though the port is used by
the client socket r in A.
If I remove a.close(), and keep a around (by passing it to the caller), a.bind
works as expected - it raises socket.error: (10048, 'Address already in use').
But in the litterature on sockets, I read it should be okay to close the server
socket and keep using the client sockets.
So, is this a possible bug in bind() ?
I have tested the new code from Tim Peters, it apparently works, ports are given
out by windows.
But could the same problem with bind occur here, since a is closed (and garbage
collected) ? (far less chance for that since we do not specify port numbers, I
know).
I tried getting a pair of sockets with Tim's code, and then trying to bind a
third socket to the same port as a/r. And I got the same problem as above.
Sune
+++++++++++++++++++++
import socket, errno
class BindError(Exception):
pass
def socktest():
"""blabla
"""
address = ('127.9.9.9', 19999)
a = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
w = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
# 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:
print port
try:
a.bind((host, port))
break
except:
if port <= 19950:
raise BindError, 'Cannot bind trigger!'
port=port - 1
a.listen (1)
w.setblocking (0)
try:
w.connect ((host, port))
except:
pass
r, addr = a.accept()
a.close()
w.setblocking (1)
#return (a, w, r)
return (w, r)
#return w
+++++++++++++++++++++
>
> Yup. ZODB has what looks like a copy/paste of this code, in
> ZEO/zrpc/trigger.py. I didn't realize where it came from originally
> until you pointed out the Medusa code here.
>
> Anyway, it so happens I rewrote ZEO's copy a few weeks ago, in ZODB
> 3.4. The Windows part is much simpler there now. I don't know why
> the original might fail in the way Sune reported, but perhaps the
> rewritten version would not.
>
> Before:
>
> # tricky: get a pair of connected sockets
> host='127.0.0.1'
> port=19999
> while 1:
> try:
> self.address=(host, port)
> a.bind(self.address)
> break
> except:
> if port <= 19950:
> raise BindError, 'Cannot bind trigger!'
> port=port - 1
>
> a.listen (1)
> w.setblocking (0)
> try:
> w.connect (self.address)
> except:
> pass
> r, addr = a.accept()
> a.close()
> w.setblocking (1)
> self.trigger = w
>
> After:
>
> # Specifying port 0 tells Windows to pick a port for us.
> a.bind(("127.0.0.1", 0))
> connect_address = a.getsockname() # assigned (host, port) pair
> a.listen(1)
> w.connect(connect_address)
> r, addr = a.accept() # r becomes asyncore's (self.)socket
> a.close()
> self.trigger = w
> _______________________________________________
> Zope maillist - Zope at 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 )
>
More information about the Zope
mailing list