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
|
From: Simon McVittie <smcv@debian.org>
Date: Tue, 8 May 2018 09:18:28 +0100
Subject: cross-test-server: Avoid a race condition in the client
There is a race condition here between these chains of events, which as
far as I can tell has existed for at least 10 years:
* server receives Tests.Trigger() and schedules SignalTests.Triggered
* server returns to main loop
* server emits SignalTests.Triggered
* client receives SignalTests.Triggered
and
* server receives Tests.Trigger() and replies with success
* client receives success and emits SignalTests.Trigger
* server receives SignalTests.Trigger and calls CallbackTests.Response()
* client receives CallbackTests.Response() and calls Tests.Exit()
* server receives Tests.Exit() and replies with success
* client quits its main loop
If we don't reply to Tests.Trigger() until after the SignalTests.Triggered
signal has been sent, because the client called Tests.Trigger()
asynchronously, messages are not re-ordered and the reply arrives after
the signal; so the whole chain of events leading up to
"client receives SignalTests.Triggered" happens before
"client receives success and emits SignalTests.Trigger" and there is
no race condition.
Bug-Debian: https://bugs.debian.org/898158
Applied-upstream: 1.2.10, commit:62789b8dd79f2b29252f31c1f424cc43e4ca5ec5
---
test/cross-test-server.py | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/test/cross-test-server.py b/test/cross-test-server.py
index 313a063..30edb1c 100755
--- a/test/cross-test-server.py
+++ b/test/cross-test-server.py
@@ -274,15 +274,18 @@ class TestsImpl(dbus.service.Object):
@dbus.service.method(INTERFACE_TESTS, 'st', '',
connection_keyword='conn',
+ async_callbacks=('reply_cb', 'error_cb'),
**kwargs)
- def Trigger(self, object, parameter, conn=None):
+ def Trigger(self, object, parameter, conn=None, reply_cb=None,
+ error_cb=None):
assert isinstance(object, str)
logger.info('method/signal: client wants me to emit Triggered(%r) from %r', parameter, object)
tested_things.add(INTERFACE_TESTS + '.Trigger')
gobject.idle_add(lambda: self.emit_Triggered_from(conn, object,
- parameter))
-
- def emit_Triggered_from(self, conn, object, parameter):
+ parameter,
+ reply_cb))
+
+ def emit_Triggered_from(self, conn, object, parameter, reply_cb):
assert isinstance(object, str)
logger.info('method/signal: Emitting Triggered(%r) from %r', parameter, object)
obj = objects.get(object, None)
@@ -291,6 +294,8 @@ class TestsImpl(dbus.service.Object):
objects[object] = obj
obj.Triggered(parameter)
logger.info('method/signal: Emitted Triggered')
+ reply_cb()
+ logger.info('method/signal: Sent reply for Tests.Trigger()')
@dbus.service.method(INTERFACE_TESTS, '', '')
def Exit(self):
|