diff options
author | Joshua Watt <jpewhacker@gmail.com> | 2021-07-22 11:19:37 -0500 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2021-07-29 23:21:24 +0100 |
commit | a83ea01b99d799025caede57512648d3258579d0 (patch) | |
tree | 72b5d1858931bc1e5f7244c9dcff36493650e8a7 /bitbake/lib/hashserv/tests.py | |
parent | 445c5b9324a6243a0a3941c5f00e369d7749efde (diff) | |
download | poky-a83ea01b99d799025caede57512648d3258579d0.tar.gz |
bitbake: bitbake: asyncrpc: Catch early SIGTERM
If the SIGTERM signal is sent to an asyncrpc server before it has
installed the SIGTERM handler in the main loop, it may miss the signal
which will can cause the calling process to wait forever on the join().
To resolve this, the calling process should mask of SIGTERM before
forking the server process and the server should unmask the signal only
after the handler is installed. To simplify the usage of the server, an
new helper function called serve_as_process() is added to do this
automatically and correctly.
Thanks: Scott Murray <scott.murray@konsulko.com> for helping debug
(Bitbake rev: ef2865efa98ad20823267364f2159d8d8c931400)
Signed-off-by: Joshua Watt <JPEWhacker@gmail.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'bitbake/lib/hashserv/tests.py')
-rw-r--r-- | bitbake/lib/hashserv/tests.py | 54 |
1 files changed, 42 insertions, 12 deletions
diff --git a/bitbake/lib/hashserv/tests.py b/bitbake/lib/hashserv/tests.py index e2b762dbf0..e851535c59 100644 --- a/bitbake/lib/hashserv/tests.py +++ b/bitbake/lib/hashserv/tests.py | |||
@@ -15,28 +15,32 @@ import tempfile | |||
15 | import threading | 15 | import threading |
16 | import unittest | 16 | import unittest |
17 | import socket | 17 | import socket |
18 | import time | ||
19 | import signal | ||
18 | 20 | ||
19 | def _run_server(server, idx): | 21 | def server_prefunc(server, idx): |
20 | # logging.basicConfig(level=logging.DEBUG, filename='bbhashserv.log', filemode='w', | 22 | logging.basicConfig(level=logging.DEBUG, filename='bbhashserv.log', filemode='w', |
21 | # format='%(levelname)s %(filename)s:%(lineno)d %(message)s') | 23 | format='%(levelname)s %(filename)s:%(lineno)d %(message)s') |
24 | server.logger.debug("Running server %d" % idx) | ||
22 | sys.stdout = open('bbhashserv-%d.log' % idx, 'w') | 25 | sys.stdout = open('bbhashserv-%d.log' % idx, 'w') |
23 | sys.stderr = sys.stdout | 26 | sys.stderr = sys.stdout |
24 | server.serve_forever() | ||
25 | |||
26 | 27 | ||
27 | class HashEquivalenceTestSetup(object): | 28 | class HashEquivalenceTestSetup(object): |
28 | METHOD = 'TestMethod' | 29 | METHOD = 'TestMethod' |
29 | 30 | ||
30 | server_index = 0 | 31 | server_index = 0 |
31 | 32 | ||
32 | def start_server(self, dbpath=None, upstream=None, read_only=False): | 33 | def start_server(self, dbpath=None, upstream=None, read_only=False, prefunc=server_prefunc): |
33 | self.server_index += 1 | 34 | self.server_index += 1 |
34 | if dbpath is None: | 35 | if dbpath is None: |
35 | dbpath = os.path.join(self.temp_dir.name, "db%d.sqlite" % self.server_index) | 36 | dbpath = os.path.join(self.temp_dir.name, "db%d.sqlite" % self.server_index) |
36 | 37 | ||
37 | def cleanup_thread(thread): | 38 | def cleanup_server(server): |
38 | thread.terminate() | 39 | if server.process.exitcode is not None: |
39 | thread.join() | 40 | return |
41 | |||
42 | server.process.terminate() | ||
43 | server.process.join() | ||
40 | 44 | ||
41 | server = create_server(self.get_server_addr(self.server_index), | 45 | server = create_server(self.get_server_addr(self.server_index), |
42 | dbpath, | 46 | dbpath, |
@@ -44,9 +48,8 @@ class HashEquivalenceTestSetup(object): | |||
44 | read_only=read_only) | 48 | read_only=read_only) |
45 | server.dbpath = dbpath | 49 | server.dbpath = dbpath |
46 | 50 | ||
47 | server.thread = multiprocessing.Process(target=_run_server, args=(server, self.server_index)) | 51 | server.serve_as_process(prefunc=prefunc, args=(self.server_index,)) |
48 | server.thread.start() | 52 | self.addCleanup(cleanup_server, server) |
49 | self.addCleanup(cleanup_thread, server.thread) | ||
50 | 53 | ||
51 | def cleanup_client(client): | 54 | def cleanup_client(client): |
52 | client.close() | 55 | client.close() |
@@ -283,6 +286,33 @@ class HashEquivalenceCommonTests(object): | |||
283 | self.assertClientGetHash(self.client, taskhash2, None) | 286 | self.assertClientGetHash(self.client, taskhash2, None) |
284 | 287 | ||
285 | 288 | ||
289 | def test_slow_server_start(self): | ||
290 | """ | ||
291 | Ensures that the server will exit correctly even if it gets a SIGTERM | ||
292 | before entering the main loop | ||
293 | """ | ||
294 | |||
295 | event = multiprocessing.Event() | ||
296 | |||
297 | def prefunc(server, idx): | ||
298 | nonlocal event | ||
299 | server_prefunc(server, idx) | ||
300 | event.wait() | ||
301 | |||
302 | def do_nothing(signum, frame): | ||
303 | pass | ||
304 | |||
305 | old_signal = signal.signal(signal.SIGTERM, do_nothing) | ||
306 | self.addCleanup(signal.signal, signal.SIGTERM, old_signal) | ||
307 | |||
308 | _, server = self.start_server(prefunc=prefunc) | ||
309 | server.process.terminate() | ||
310 | time.sleep(30) | ||
311 | event.set() | ||
312 | server.process.join(300) | ||
313 | self.assertIsNotNone(server.process.exitcode, "Server did not exit in a timely manner!") | ||
314 | |||
315 | |||
286 | class TestHashEquivalenceUnixServer(HashEquivalenceTestSetup, HashEquivalenceCommonTests, unittest.TestCase): | 316 | class TestHashEquivalenceUnixServer(HashEquivalenceTestSetup, HashEquivalenceCommonTests, unittest.TestCase): |
287 | def get_server_addr(self, server_idx): | 317 | def get_server_addr(self, server_idx): |
288 | return "unix://" + os.path.join(self.temp_dir.name, 'sock%d' % server_idx) | 318 | return "unix://" + os.path.join(self.temp_dir.name, 'sock%d' % server_idx) |