portal 3.4.4__tar.gz → 3.5.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {portal-3.4.4/portal.egg-info → portal-3.5.1}/PKG-INFO +1 -1
- {portal-3.4.4 → portal-3.5.1}/portal/__init__.py +1 -1
- {portal-3.4.4 → portal-3.5.1}/portal/client_socket.py +43 -35
- {portal-3.4.4 → portal-3.5.1}/portal/server_socket.py +20 -19
- {portal-3.4.4 → portal-3.5.1/portal.egg-info}/PKG-INFO +1 -1
- {portal-3.4.4 → portal-3.5.1}/LICENSE +0 -0
- {portal-3.4.4 → portal-3.5.1}/MANIFEST.in +0 -0
- {portal-3.4.4 → portal-3.5.1}/README.md +0 -0
- {portal-3.4.4 → portal-3.5.1}/portal/batching.py +0 -0
- {portal-3.4.4 → portal-3.5.1}/portal/buffers.py +0 -0
- {portal-3.4.4 → portal-3.5.1}/portal/client.py +0 -0
- {portal-3.4.4 → portal-3.5.1}/portal/contextlib.py +0 -0
- {portal-3.4.4 → portal-3.5.1}/portal/packlib.py +0 -0
- {portal-3.4.4 → portal-3.5.1}/portal/poollib.py +0 -0
- {portal-3.4.4 → portal-3.5.1}/portal/process.py +0 -0
- {portal-3.4.4 → portal-3.5.1}/portal/server.py +0 -0
- {portal-3.4.4 → portal-3.5.1}/portal/sharray.py +0 -0
- {portal-3.4.4 → portal-3.5.1}/portal/thread.py +0 -0
- {portal-3.4.4 → portal-3.5.1}/portal/utils.py +0 -0
- {portal-3.4.4 → portal-3.5.1}/portal.egg-info/SOURCES.txt +0 -0
- {portal-3.4.4 → portal-3.5.1}/portal.egg-info/dependency_links.txt +0 -0
- {portal-3.4.4 → portal-3.5.1}/portal.egg-info/requires.txt +0 -0
- {portal-3.4.4 → portal-3.5.1}/portal.egg-info/top_level.txt +0 -0
- {portal-3.4.4 → portal-3.5.1}/pyproject.toml +0 -0
- {portal-3.4.4 → portal-3.5.1}/requirements.txt +0 -0
- {portal-3.4.4 → portal-3.5.1}/setup.cfg +0 -0
- {portal-3.4.4 → portal-3.5.1}/setup.py +0 -0
- {portal-3.4.4 → portal-3.5.1}/tests/test_batching.py +0 -0
- {portal-3.4.4 → portal-3.5.1}/tests/test_client.py +0 -0
- {portal-3.4.4 → portal-3.5.1}/tests/test_errfile.py +0 -0
- {portal-3.4.4 → portal-3.5.1}/tests/test_pack.py +0 -0
- {portal-3.4.4 → portal-3.5.1}/tests/test_process.py +0 -0
- {portal-3.4.4 → portal-3.5.1}/tests/test_server.py +0 -0
- {portal-3.4.4 → portal-3.5.1}/tests/test_socket.py +0 -0
- {portal-3.4.4 → portal-3.5.1}/tests/test_thread.py +0 -0
@@ -1,5 +1,6 @@
|
|
1
1
|
import collections
|
2
2
|
import dataclasses
|
3
|
+
import os
|
3
4
|
import queue
|
4
5
|
import select
|
5
6
|
import socket
|
@@ -30,7 +31,6 @@ class Options:
|
|
30
31
|
logging: bool = True
|
31
32
|
logging_color: str = 'yellow'
|
32
33
|
connect_wait: float = 0.1
|
33
|
-
idle_sleep: float = 0.0001
|
34
34
|
|
35
35
|
|
36
36
|
class ClientSocket:
|
@@ -52,6 +52,7 @@ class ClientSocket:
|
|
52
52
|
self.wantconn = threading.Event()
|
53
53
|
self.sendq = collections.deque()
|
54
54
|
self.recvq = queue.Queue()
|
55
|
+
self.get_signal, self.set_signal = os.pipe()
|
55
56
|
|
56
57
|
self.running = True
|
57
58
|
self.thread = thread.Thread(self._loop, name=f'{name}Loop')
|
@@ -76,6 +77,7 @@ class ClientSocket:
|
|
76
77
|
self.require_connection(timeout)
|
77
78
|
maxsize = self.options.max_msg_size
|
78
79
|
self.sendq.append(buffers.SendBuffer(*data, maxsize=maxsize))
|
80
|
+
os.write(self.set_signal, bytes(1))
|
79
81
|
|
80
82
|
def recv(self, timeout=None):
|
81
83
|
assert self.running
|
@@ -98,6 +100,8 @@ class ClientSocket:
|
|
98
100
|
self.running = False
|
99
101
|
self.thread.join(timeout)
|
100
102
|
self.thread.kill()
|
103
|
+
os.close(self.get_signal)
|
104
|
+
os.close(self.set_signal)
|
101
105
|
|
102
106
|
def require_connection(self, timeout):
|
103
107
|
if self.connected:
|
@@ -111,7 +115,9 @@ class ClientSocket:
|
|
111
115
|
recvbuf = buffers.RecvBuffer(maxsize=self.options.max_msg_size)
|
112
116
|
sock = None
|
113
117
|
poll = select.poll()
|
118
|
+
poll.register(self.get_signal, select.POLLIN)
|
114
119
|
isconn = False # Local mirror of self.isconn without the lock.
|
120
|
+
writing = False
|
115
121
|
|
116
122
|
while self.running or (self.sendq and isconn):
|
117
123
|
|
@@ -121,7 +127,7 @@ class ClientSocket:
|
|
121
127
|
sock = self._connect()
|
122
128
|
if not sock:
|
123
129
|
break
|
124
|
-
poll.register(sock, select.POLLIN
|
130
|
+
poll.register(sock, select.POLLIN)
|
125
131
|
self.isconn.set()
|
126
132
|
isconn = True
|
127
133
|
if not self.options.autoconn:
|
@@ -130,36 +136,37 @@ class ClientSocket:
|
|
130
136
|
|
131
137
|
try:
|
132
138
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
if self.sendq
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
139
|
+
if not writing:
|
140
|
+
fds = [fd for fd, _ in poll.poll(0.2)]
|
141
|
+
if self.get_signal in fds:
|
142
|
+
writing = True
|
143
|
+
os.read(self.get_signal, 1)
|
144
|
+
|
145
|
+
try:
|
146
|
+
recvbuf.recv(sock)
|
147
|
+
if recvbuf.done():
|
148
|
+
if self.recvq.qsize() > self.options.max_recv_queue:
|
149
|
+
raise RuntimeError('Too many incoming messages enqueued')
|
150
|
+
msg = recvbuf.result()
|
151
|
+
self.recvq.put(msg)
|
152
|
+
[x(msg) for x in self.callbacks_recv]
|
153
|
+
recvbuf = buffers.RecvBuffer(maxsize=self.options.max_msg_size)
|
154
|
+
except BlockingIOError:
|
155
|
+
pass
|
156
|
+
|
157
|
+
if self.sendq:
|
158
|
+
try:
|
159
|
+
self.sendq[0].send(sock)
|
160
|
+
if self.sendq[0].done():
|
161
|
+
self.sendq.popleft()
|
162
|
+
if not self.sendq:
|
163
|
+
writing = False
|
164
|
+
except BlockingIOError:
|
165
|
+
pass
|
166
|
+
except ConnectionResetError:
|
167
|
+
# The server is gone but we may have buffered messages left to
|
168
|
+
# read, so we keep the socket open until recv() fails.
|
169
|
+
pass
|
163
170
|
|
164
171
|
except OSError as e:
|
165
172
|
detail = f'{type(e).__name__}'
|
@@ -231,9 +238,10 @@ class ClientSocket:
|
|
231
238
|
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, after)
|
232
239
|
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, every)
|
233
240
|
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, fails)
|
234
|
-
|
235
|
-
|
236
|
-
|
241
|
+
if hasattr(socket, 'TCP_USER_TIMEOUT'): # Linux
|
242
|
+
sock.setsockopt(
|
243
|
+
socket.IPPROTO_TCP, socket.TCP_USER_TIMEOUT,
|
244
|
+
1000 * (after + every * fails))
|
237
245
|
if sys.platform == 'darwin':
|
238
246
|
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
|
239
247
|
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPALIVE, every)
|
@@ -1,9 +1,9 @@
|
|
1
1
|
import collections
|
2
2
|
import dataclasses
|
3
|
+
import os
|
3
4
|
import queue
|
4
5
|
import selectors
|
5
6
|
import socket
|
6
|
-
import time
|
7
7
|
|
8
8
|
from . import buffers
|
9
9
|
from . import contextlib
|
@@ -32,7 +32,6 @@ class Options:
|
|
32
32
|
max_send_queue: int = 4096
|
33
33
|
logging: bool = True
|
34
34
|
logging_color: str = 'blue'
|
35
|
-
idle_sleep: float = 0.0001
|
36
35
|
|
37
36
|
|
38
37
|
class ServerSocket:
|
@@ -56,7 +55,9 @@ class ServerSocket:
|
|
56
55
|
self.sock.bind(self.addr)
|
57
56
|
self.sock.setblocking(False)
|
58
57
|
self.sock.listen(8192)
|
58
|
+
self.get_signal, self.set_signal = os.pipe()
|
59
59
|
self.sel = selectors.DefaultSelector()
|
60
|
+
self.sel.register(self.get_signal, selectors.EVENT_READ, data='signal')
|
60
61
|
self.sel.register(self.sock, selectors.EVENT_READ, data=None)
|
61
62
|
self._log(f'Listening at {self.addr[0]}:{self.addr[1]}')
|
62
63
|
self.conns = {}
|
@@ -89,6 +90,7 @@ class ServerSocket:
|
|
89
90
|
try:
|
90
91
|
self.conns[addr].sendbufs.append(
|
91
92
|
buffers.SendBuffer(*data, maxsize=maxsize))
|
93
|
+
os.write(self.set_signal, bytes(1))
|
92
94
|
except KeyError:
|
93
95
|
self._log('Dropping message to disconnected client')
|
94
96
|
|
@@ -101,38 +103,38 @@ class ServerSocket:
|
|
101
103
|
[conn.sock.close() for conn in self.conns.values()]
|
102
104
|
self.sock.close()
|
103
105
|
self.sel.close()
|
106
|
+
os.close(self.get_signal)
|
107
|
+
os.close(self.set_signal)
|
104
108
|
|
105
109
|
def _loop(self):
|
110
|
+
writing = False
|
106
111
|
try:
|
107
112
|
while self.running or self._numsending():
|
108
|
-
idle = True
|
109
|
-
writeable = []
|
110
113
|
for key, mask in self.sel.select(timeout=0.2):
|
111
|
-
if key.data
|
114
|
+
if key.data == 'signal':
|
115
|
+
writing = True
|
116
|
+
os.read(self.get_signal, 1)
|
117
|
+
elif key.data is None and self.reading:
|
112
118
|
assert mask & selectors.EVENT_READ
|
113
119
|
self._accept(key.fileobj)
|
114
|
-
idle = False
|
115
120
|
elif mask & selectors.EVENT_READ and self.reading:
|
116
121
|
self._recv(key.data)
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
for conn in
|
121
|
-
if not conn.sendbufs:
|
122
|
-
continue
|
122
|
+
if not writing:
|
123
|
+
continue
|
124
|
+
pending = [conn for conn in self.conns.values() if conn.sendbufs]
|
125
|
+
for conn in pending:
|
123
126
|
try:
|
124
127
|
conn.sendbufs[0].send(conn.sock)
|
125
|
-
idle = False
|
126
128
|
if conn.sendbufs[0].done():
|
127
129
|
conn.sendbufs.popleft()
|
130
|
+
if not any(conn.sendbufs for conn in pending):
|
131
|
+
writing = False
|
128
132
|
except BlockingIOError:
|
129
133
|
pass
|
130
134
|
except ConnectionResetError:
|
131
|
-
# The client is gone but we may have buffered messages left
|
132
|
-
# read, so we keep the socket open until recv() fails.
|
135
|
+
# The client is gone but we may have buffered messages left
|
136
|
+
# to read, so we keep the socket open until recv() fails.
|
133
137
|
pass
|
134
|
-
if idle and self.options.idle_sleep:
|
135
|
-
time.sleep(self.options.idle_sleep)
|
136
138
|
except Exception as e:
|
137
139
|
self.error = e
|
138
140
|
|
@@ -141,8 +143,7 @@ class ServerSocket:
|
|
141
143
|
self._log(f'Accepted connection from {addr[0]}:{addr[1]}')
|
142
144
|
sock.setblocking(False)
|
143
145
|
conn = Connection(sock, addr)
|
144
|
-
self.sel.register(
|
145
|
-
sock, selectors.EVENT_READ | selectors.EVENT_WRITE, data=conn)
|
146
|
+
self.sel.register(sock, selectors.EVENT_READ, data=conn)
|
146
147
|
self.conns[addr] = conn
|
147
148
|
|
148
149
|
def _recv(self, conn):
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|