1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

import errno 

from functools import partial 

import select 

import sys 

 

try: 

from time import monotonic 

except ImportError: 

from time import time as monotonic 

 

__all__ = ["NoWayToWaitForSocketError", "wait_for_read", "wait_for_write"] 

 

 

class NoWayToWaitForSocketError(Exception): 

pass 

 

 

# How should we wait on sockets? 

# 

# There are two types of APIs you can use for waiting on sockets: the fancy 

# modern stateful APIs like epoll/kqueue, and the older stateless APIs like 

# select/poll. The stateful APIs are more efficient when you have a lots of 

# sockets to keep track of, because you can set them up once and then use them 

# lots of times. But we only ever want to wait on a single socket at a time 

# and don't want to keep track of state, so the stateless APIs are actually 

# more efficient. So we want to use select() or poll(). 

# 

# Now, how do we choose between select() and poll()? On traditional Unixes, 

# select() has a strange calling convention that makes it slow, or fail 

# altogether, for high-numbered file descriptors. The point of poll() is to fix 

# that, so on Unixes, we prefer poll(). 

# 

# On Windows, there is no poll() (or at least Python doesn't provide a wrapper 

# for it), but that's OK, because on Windows, select() doesn't have this 

# strange calling convention; plain select() works fine. 

# 

# So: on Windows we use select(), and everywhere else we use poll(). We also 

# fall back to select() in case poll() is somehow broken or missing. 

 

if sys.version_info >= (3, 5): 

# Modern Python, that retries syscalls by default 

def _retry_on_intr(fn, timeout): 

return fn(timeout) 

 

 

else: 

# Old and broken Pythons. 

def _retry_on_intr(fn, timeout): 

if timeout is None: 

deadline = float("inf") 

else: 

deadline = monotonic() + timeout 

 

while True: 

try: 

return fn(timeout) 

# OSError for 3 <= pyver < 3.5, select.error for pyver <= 2.7 

except (OSError, select.error) as e: 

# 'e.args[0]' incantation works for both OSError and select.error 

if e.args[0] != errno.EINTR: 

raise 

else: 

timeout = deadline - monotonic() 

if timeout < 0: 

timeout = 0 

if timeout == float("inf"): 

timeout = None 

continue 

 

 

def select_wait_for_socket(sock, read=False, write=False, timeout=None): 

if not read and not write: 

raise RuntimeError("must specify at least one of read=True, write=True") 

rcheck = [] 

wcheck = [] 

if read: 

rcheck.append(sock) 

if write: 

wcheck.append(sock) 

# When doing a non-blocking connect, most systems signal success by 

# marking the socket writable. Windows, though, signals success by marked 

# it as "exceptional". We paper over the difference by checking the write 

# sockets for both conditions. (The stdlib selectors module does the same 

# thing.) 

fn = partial(select.select, rcheck, wcheck, wcheck) 

rready, wready, xready = _retry_on_intr(fn, timeout) 

return bool(rready or wready or xready) 

 

 

def poll_wait_for_socket(sock, read=False, write=False, timeout=None): 

if not read and not write: 

raise RuntimeError("must specify at least one of read=True, write=True") 

mask = 0 

if read: 

mask |= select.POLLIN 

if write: 

mask |= select.POLLOUT 

poll_obj = select.poll() 

poll_obj.register(sock, mask) 

 

# For some reason, poll() takes timeout in milliseconds 

def do_poll(t): 

if t is not None: 

t *= 1000 

return poll_obj.poll(t) 

 

return bool(_retry_on_intr(do_poll, timeout)) 

 

 

def null_wait_for_socket(*args, **kwargs): 

raise NoWayToWaitForSocketError("no select-equivalent available") 

 

 

def _have_working_poll(): 

# Apparently some systems have a select.poll that fails as soon as you try 

# to use it, either due to strange configuration or broken monkeypatching 

# from libraries like eventlet/greenlet. 

try: 

poll_obj = select.poll() 

_retry_on_intr(poll_obj.poll, 0) 

except (AttributeError, OSError): 

return False 

else: 

return True 

 

 

def wait_for_socket(*args, **kwargs): 

# We delay choosing which implementation to use until the first time we're 

# called. We could do it at import time, but then we might make the wrong 

# decision if someone goes wild with monkeypatching select.poll after 

# we're imported. 

global wait_for_socket 

if _have_working_poll(): 

wait_for_socket = poll_wait_for_socket 

elif hasattr(select, "select"): 

wait_for_socket = select_wait_for_socket 

else: # Platform-specific: Appengine. 

wait_for_socket = null_wait_for_socket 

return wait_for_socket(*args, **kwargs) 

 

 

def wait_for_read(sock, timeout=None): 

""" Waits for reading to be available on a given socket. 

Returns True if the socket is readable, or False if the timeout expired. 

""" 

return wait_for_socket(sock, read=True, timeout=timeout) 

 

 

def wait_for_write(sock, timeout=None): 

""" Waits for writing to be available on a given socket. 

Returns True if the socket is readable, or False if the timeout expired. 

""" 

return wait_for_socket(sock, write=True, timeout=timeout)