mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2025-02-03 13:14:31 +03:00
Remove TcpClient.write/read shared locks
Since they were shared between write and read, and now the read is done constantly on a separate thread, the read lock would cause the write method to be locked and not functional at all
This commit is contained in:
parent
43b79c3d36
commit
cc280a129d
|
@ -65,25 +65,26 @@ class TcpClient:
|
||||||
def write(self, data):
|
def write(self, data):
|
||||||
"""Writes (sends) the specified bytes to the connected peer"""
|
"""Writes (sends) the specified bytes to the connected peer"""
|
||||||
|
|
||||||
# Ensure that only one thread can send data at once
|
# TODO Check whether the code using this has multiple threads calling
|
||||||
with self._lock:
|
# .write() on the very same socket. If so, have two locks, one for
|
||||||
try:
|
# .write() and another for .read().
|
||||||
view = memoryview(data)
|
try:
|
||||||
total_sent, total = 0, len(data)
|
view = memoryview(data)
|
||||||
while total_sent < total:
|
total_sent, total = 0, len(data)
|
||||||
try:
|
while total_sent < total:
|
||||||
sent = self._socket.send(view[total_sent:])
|
try:
|
||||||
if sent == 0:
|
sent = self._socket.send(view[total_sent:])
|
||||||
self.close()
|
if sent == 0:
|
||||||
raise ConnectionResetError(
|
self.close()
|
||||||
'The server has closed the connection.')
|
raise ConnectionResetError(
|
||||||
total_sent += sent
|
'The server has closed the connection.')
|
||||||
|
total_sent += sent
|
||||||
|
|
||||||
except BlockingIOError:
|
except BlockingIOError:
|
||||||
time.sleep(self.delay)
|
time.sleep(self.delay)
|
||||||
except BrokenPipeError:
|
except BrokenPipeError:
|
||||||
self.close()
|
self.close()
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def read(self, size, timeout=timedelta(seconds=5)):
|
def read(self, size, timeout=timedelta(seconds=5)):
|
||||||
"""Reads (receives) a whole block of 'size bytes
|
"""Reads (receives) a whole block of 'size bytes
|
||||||
|
@ -95,47 +96,45 @@ class TcpClient:
|
||||||
operation. Set to None for no timeout
|
operation. Set to None for no timeout
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Ensure that only one thread can receive data at once
|
# Ensure it is not cancelled at first, so we can enter the loop
|
||||||
with self._lock:
|
self.cancelled.clear()
|
||||||
# Ensure it is not cancelled at first, so we can enter the loop
|
|
||||||
self.cancelled.clear()
|
|
||||||
|
|
||||||
# Set the starting time so we can
|
# Set the starting time so we can
|
||||||
# calculate whether the timeout should fire
|
# calculate whether the timeout should fire
|
||||||
start_time = datetime.now() if timeout is not None else None
|
start_time = datetime.now() if timeout is not None else None
|
||||||
|
|
||||||
with BufferedWriter(BytesIO(), buffer_size=size) as buffer:
|
with BufferedWriter(BytesIO(), buffer_size=size) as buffer:
|
||||||
bytes_left = size
|
bytes_left = size
|
||||||
while bytes_left != 0:
|
while bytes_left != 0:
|
||||||
# Only do cancel if no data was read yet
|
# Only do cancel if no data was read yet
|
||||||
# Otherwise, carry on reading and finish
|
# Otherwise, carry on reading and finish
|
||||||
if self.cancelled.is_set() and bytes_left == size:
|
if self.cancelled.is_set() and bytes_left == size:
|
||||||
raise ReadCancelledError()
|
raise ReadCancelledError()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
partial = self._socket.recv(bytes_left)
|
partial = self._socket.recv(bytes_left)
|
||||||
if len(partial) == 0:
|
if len(partial) == 0:
|
||||||
self.close()
|
self.close()
|
||||||
raise ConnectionResetError(
|
raise ConnectionResetError(
|
||||||
'The server has closed the connection.')
|
'The server has closed the connection.')
|
||||||
|
|
||||||
buffer.write(partial)
|
buffer.write(partial)
|
||||||
bytes_left -= len(partial)
|
bytes_left -= len(partial)
|
||||||
|
|
||||||
except BlockingIOError as error:
|
except BlockingIOError as error:
|
||||||
# No data available yet, sleep a bit
|
# No data available yet, sleep a bit
|
||||||
time.sleep(self.delay)
|
time.sleep(self.delay)
|
||||||
|
|
||||||
# Check if the timeout finished
|
# Check if the timeout finished
|
||||||
if timeout is not None:
|
if timeout is not None:
|
||||||
time_passed = datetime.now() - start_time
|
time_passed = datetime.now() - start_time
|
||||||
if time_passed > timeout:
|
if time_passed > timeout:
|
||||||
raise TimeoutError(
|
raise TimeoutError(
|
||||||
'The read operation exceeded the timeout.') from error
|
'The read operation exceeded the timeout.') from error
|
||||||
|
|
||||||
# If everything went fine, return the read bytes
|
# If everything went fine, return the read bytes
|
||||||
buffer.flush()
|
buffer.flush()
|
||||||
return buffer.raw.getvalue()
|
return buffer.raw.getvalue()
|
||||||
|
|
||||||
def cancel_read(self):
|
def cancel_read(self):
|
||||||
"""Cancels the read operation IF it hasn't yet
|
"""Cancels the read operation IF it hasn't yet
|
||||||
|
|
Loading…
Reference in New Issue
Block a user