summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames E. Blair <jeblair@redhat.com>2017-09-30 08:45:01 -0700
committerJames E. Blair <jeblair@redhat.com>2017-09-30 09:18:27 -0700
commitb24b4d2f6ee27efcf293db87423b901cedac3d7b (patch)
tree9287a7b20d55b5dce3e2d621df187aeb01c092ac
parentbf8d96cb7732bdd265d0360652c2df269d356ea9 (diff)
downloadgear-b24b4d2f6ee27efcf293db87423b901cedac3d7b.tar.gz
Add a send lock to the base Connection class
The sendRaw method (and therefore sendPacket) was originally thread safe by virtue of consisting of a single socket.send() call, but when we added SSL, we added a loop within the method to handle the increased likelihood that not all data would be sent in one call. Of course, the method should have been written this way to start with. However, this means that we can end up with partial packets being sent before a context switch to another thread which may also want to send a packet. To handle that case, place the entire method in a lock. Note, this doesn't affect server connections as they use a non-blocking connection which has a send queue, so only one thread ever actuall transmits. Change-Id: I3bda6fda5f762d18f28b56a43b7dc28f37dbc427
-rw-r--r--gear/__init__.py24
1 files changed, 13 insertions, 11 deletions
diff --git a/gear/__init__.py b/gear/__init__.py
index ae99eb8..e338efc 100644
--- a/gear/__init__.py
+++ b/gear/__init__.py
@@ -142,6 +142,7 @@ class Connection(object):
self.input_buffer = b''
self.need_bytes = False
self.echo_lock = threading.Lock()
+ self.send_lock = threading.Lock()
self._init()
def _init(self):
@@ -237,17 +238,18 @@ class Connection(object):
:arg bytes data The raw data to send
"""
- while True:
- try:
- self.conn.send(data)
- except ssl.SSLError as e:
- if e.errno == ssl.SSL_ERROR_WANT_READ:
- continue
- elif e.errno == ssl.SSL_ERROR_WANT_WRITE:
- continue
- else:
- raise
- break
+ with self.send_lock:
+ sent = 0
+ while sent < len(data):
+ try:
+ sent += self.conn.send(data)
+ except ssl.SSLError as e:
+ if e.errno == ssl.SSL_ERROR_WANT_READ:
+ continue
+ elif e.errno == ssl.SSL_ERROR_WANT_WRITE:
+ continue
+ else:
+ raise
def sendPacket(self, packet):
"""Send a packet to the server.