[Pkg-bitcoin-commits] [python-quamash] 181/269: Implement replacement of existing readers/writers

Jonas Smedegaard dr at jones.dk
Fri Nov 24 11:26:30 UTC 2017


This is an automated email from the git hooks/post-receive script.

js pushed a commit to branch master
in repository python-quamash.

commit d8940fd352e7ae66bb0bc19644b914b77a0a6d19
Author: Jonas Wielicki <j.wielicki at sotecware.net>
Date:   Fri Jan 9 20:58:13 2015 +0100

    Implement replacement of existing readers/writers
    
    The behaviour of calling add_reader/add_writer on the same fd without
    removing the reader/writer before is currently quite erratic. It is
    seemingly undefined which reader/writer gets called, the old or the new,
    as the previous notifier is not disabled and disconnected properly.
    
    Note: actually I would expect that garbage collection of the notifier is
    sufficient; this does not seem to be the case. It is certainly possible
    though that garbage collection does not happen in time, so in any case
    it is safer to disable and disconnect it beforehands.
    
    Note: only disabling was not sufficient, for unknown reasons.
    
    The new behaviour conforms to [PEP 3156, Section "I/O-Callbacks"][0]:
    
    > ``add_reader(fd, callback, *args)``. Arrange for ``callback(*args)``
    > to be called whenever file descriptor ``fd`` is deemed ready for
    > reading. Calling ``add_reader()`` again for the same file descriptor
    > implies a call to ``remove_reader()`` for the same file descriptor.
    
    Analogous to double remove of a reader/writer, this still issues a
    warning.
    
       [0]: https://www.python.org/dev/peps/pep-3156/#i-o-callbacks
---
 quamash/__init__.py      | 26 ++++++++++++++
 tests/test_qeventloop.py | 90 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 116 insertions(+)

diff --git a/quamash/__init__.py b/quamash/__init__.py
index 55f389a..0864924 100644
--- a/quamash/__init__.py
+++ b/quamash/__init__.py
@@ -359,6 +359,19 @@ class QEventLoop(_baseclass):
 
 	def add_reader(self, fd, callback, *args):
 		"""Register a callback for when a file descriptor is ready for reading."""
+		try:
+			existing = self._read_notifiers[fd]
+		except KeyError:
+			pass
+		else:
+			# this is neccessary to avoid race condition-like issues
+			existing.setEnabled(False)
+			existing.activated.disconnect()
+			# will get overwritten by the assignment below anyways
+			self._logger.warning(
+				'There is already a reader attached for fd {}'.format(fd)
+			)
+
 		notifier = QtCore.QSocketNotifier(fd, QtCore.QSocketNotifier.Read)
 		notifier.setEnabled(True)
 		self._logger.debug('Adding reader callback for file descriptor {}'.format(fd))
@@ -382,6 +395,19 @@ class QEventLoop(_baseclass):
 
 	def add_writer(self, fd, callback, *args):
 		"""Register a callback for when a file descriptor is ready for writing."""
+		try:
+			existing = self._write_notifiers[fd]
+		except KeyError:
+			pass
+		else:
+			# this is neccessary to avoid race condition-like issues
+			existing.setEnabled(False)
+			existing.activated.disconnect()
+			# will get overwritten by the assignment below anyways
+			self._logger.warning(
+				'There is already a writer attached for fd {}'.format(fd)
+			)
+
 		notifier = QtCore.QSocketNotifier(fd, QtCore.QSocketNotifier.Write)
 		notifier.setEnabled(True)
 		self._logger.debug('Adding writer callback for file descriptor {}'.format(fd))
diff --git a/tests/test_qeventloop.py b/tests/test_qeventloop.py
index 1846c8c..411f3ff 100644
--- a/tests/test_qeventloop.py
+++ b/tests/test_qeventloop.py
@@ -521,3 +521,93 @@ def test_regression_bug13(loop, sock_pair):
 	loop.run_until_complete(asyncio.wait_for(both_done, timeout=1.0))
 	assert result1 == b'1'
 	assert result3 == b'3'
+
+
+
+def test_add_reader_replace(loop, sock_pair):
+	c_sock, s_sock = sock_pair
+	callback_invoked = asyncio.Future()
+
+	called1 = False
+	called2 = False
+
+	def any_callback():
+		if not callback_invoked.done():
+			callback_invoked.set_result(True)
+		loop.remove_reader(c_sock.fileno())
+
+	def callback1():
+		# the "bad" callback: if this gets invoked, something went wrong
+		nonlocal called1
+		called1 = True
+		any_callback()
+
+	def callback2():
+		# the "good" callback: this is the one which should get called
+		nonlocal called2
+		called2 = True
+		any_callback()
+
+	@asyncio.coroutine
+	def server_coro():
+		s_reader, s_writer = yield from asyncio.open_connection(
+			sock=s_sock)
+		s_writer.write(b"foo")
+		yield from s_writer.drain()
+
+	@asyncio.coroutine
+	def client_coro():
+		loop.add_reader(c_sock.fileno(), callback1)
+		loop.add_reader(c_sock.fileno(), callback2)
+		yield from callback_invoked
+		loop.remove_reader(c_sock.fileno())
+		assert (yield from loop.sock_recv(c_sock, 3)) == b"foo"
+
+	client_done = asyncio.async(client_coro())
+	server_done = asyncio.async(server_coro())
+
+	both_done = asyncio.wait([server_done, client_done],
+				 return_when=asyncio.FIRST_EXCEPTION)
+	loop.run_until_complete(asyncio.wait_for(both_done, timeout=0.1))
+	assert not called1
+	assert called2
+
+
+def test_add_writer_replace(loop, sock_pair):
+	c_sock, s_sock = sock_pair
+	callback_invoked = asyncio.Future()
+
+	called1 = False
+	called2 = False
+
+	def any_callback():
+		if not callback_invoked.done():
+			callback_invoked.set_result(True)
+		loop.remove_writer(c_sock.fileno())
+
+	def callback1():
+		# the "bad" callback: if this gets invoked, something went wrong
+		nonlocal called1
+		called1 = True
+		any_callback()
+
+	def callback2():
+		# the "good" callback: this is the one which should get called
+		nonlocal called2
+		called2 = True
+		any_callback()
+
+	@asyncio.coroutine
+	def client_coro():
+		loop.add_writer(c_sock.fileno(), callback1)
+		loop.add_writer(c_sock.fileno(), callback2)
+		yield from callback_invoked
+		loop.remove_writer(c_sock.fileno())
+
+	client_done = asyncio.async(client_coro())
+
+	both_done = asyncio.wait([client_done],
+				 return_when=asyncio.FIRST_EXCEPTION)
+	loop.run_until_complete(asyncio.wait_for(both_done, timeout=0.1))
+	assert not called1
+	assert called2

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-bitcoin/python-quamash.git



More information about the Pkg-bitcoin-commits mailing list