diff --git a/apps/sam/python/src/i2p/select.py b/apps/sam/python/src/i2p/select.py index 9e0a0e0f4..a2aa94cbc 100644 --- a/apps/sam/python/src/i2p/select.py +++ b/apps/sam/python/src/i2p/select.py @@ -67,71 +67,99 @@ def poll(): """ return Poll() +def _has_data(S): + """True if the given I2P socket has data waiting.""" + try: + S.recv(1, i2p.socket.MSG_PEEK | i2p.socket.MSG_DONTWAIT) + return True + except: + return False + +def _noblock_select(readlist, writelist, errlist): + """Makes a single query of the given sockets, like + select() with timeout 0.0.""" + Rans = [] + Wans = [] + Eans = [] + + # Check for read availability. + for R in readlist: + if isinstance(R, int) or hasattr(R, 'fileno'): + # Python socket + if len(pyselect.select([R], [], [], 0.0)[0]) > 0: + Rans.append(R) + else: + # SAM Socket + if _has_data(R): + Rans.append(R) +# if R.type == i2p.socket.SOCK_STREAM: +# try: +# R._verify_connected() +# Rans.append(R) +# except: +# pass +# else: +# if len(R.sessobj) > 0: Rans.append(R) + + # Check for write availability. + for W in writelist: + if isinstance(W, int) or hasattr(W, 'fileno'): + # Python socket + if len(pyselect.select([], [W], [], 0.0)[1]) > 0: + Wans.append(W) + else: + # SAM Socket + if W.type == i2p.socket.SOCK_STREAM: + try: + W._verify_connected() + Wans.append(W) + except: + pass + else: + Wans.append(W) + + # Check for error conditions. + # These can only be stream errors. + for E in errlist: + if isinstance(E, int) or hasattr(E, 'fileno'): + # Python socket + if len(pyselect.select([], [], [E], 0.0)[2]) > 0: + Eans.append(E) + else: + if E.type == i2p.socket.SOCK_STREAM: + try: + # FIXME: Use a ._get_error() function for errors. + # Socket can only have an error if it connected. + E._verify_connected() + if E.sessobj.err != None: + Eans.append(E) + except: + pass + + return (Rans, Wans, Eans) + + def select(readlist, writelist, errlist, timeout=None): """Performs a select call. Works on SAM sockets and Python sockets. See select.select() in the Python library for more information.""" - Rans = [] - Wans = [] - Eans = [] + if timeout != None: end = time.clock() + timeout while True: # FIXME: Check performance. # Use pyselect.poll for Python sockets, if needed for speed. + (Rans, Wans, Eans) = _noblock_select(readlist,writelist,errlist) - # Check for read availability. - for R in readlist: - if isinstance(R, int) or hasattr(R, 'fileno'): - # Python socket - if len(pyselect.select([R], [], [], 0.0)[0]) > 0: - Rans.append(R) - else: - # SAM Socket - if R.type == i2p.socket.SOCK_STREAM: - try: - R._verify_connected() - Rans.append(R) - except: - pass - else: - if len(R) > 0: Rans.append(R) - - # Check for write availability. - for W in writelist: - if isinstance(W, int) or hasattr(W, 'fileno'): - # Python socket - if len(pyselect.select([], [W], [], 0.0)[1]) > 0: - Wans.append(W) - else: - # SAM Socket - if W.type == i2p.socket.SOCK_STREAM: - try: - W._verify_connected() - Wans.append(W) - except: - pass - else: - Wans.append(W) - - # Check for error conditions. - # These can only be stream errors. - for E in errlist: - if isinstance(E, int) or hasattr(E, 'fileno'): - # Python socket - if len(pyselect.select([], [], [E], 0.0)[2]) > 0: - Eans.append(E) - else: - if E.type == i2p.socket.SOCK_STREAM: - try: - # FIXME: Use a ._get_error() function for errors. - # Socket can only have an error if it connected. - E._verify_connected() - if E.sessobj.err != None: - Eans.append(E) - except: - pass if timeout != None and time.clock() >= end: break - if len(Rans) != 0 or len(Wans) != 0 or len(Eans) != 0: break + if len(Rans) != 0 or len(Wans) != 0 or len(Eans) != 0: + # One or more sockets are ready. + if timeout != 0.0: + # Check again, because sockets may have changed state while + # we did _noblock_select (it's safer to check twice, since + # they usually go from no data => data ready, and so forth). + (Rans, Wans, Eans) = _noblock_select(readlist, writelist, + errlist) + return (Rans, Wans, Eans) time.sleep(0.01)