xref: /openbsd/gnu/llvm/llvm/utils/lit/lit/worker.py (revision 73471bf0)
1"""
2The functions in this module are meant to run on a separate worker process.
3Exception: in single process mode _execute is called directly.
4
5For efficiency, we copy all data needed to execute all tests into each worker
6and store it in global variables. This reduces the cost of each task.
7"""
8import contextlib
9import os
10import signal
11import time
12import traceback
13
14import lit.Test
15import lit.util
16
17
18_lit_config = None
19_parallelism_semaphores = None
20
21
22def initialize(lit_config, parallelism_semaphores):
23    """Copy data shared by all test executions into worker processes"""
24    global _lit_config
25    global _parallelism_semaphores
26    _lit_config = lit_config
27    _parallelism_semaphores = parallelism_semaphores
28
29    # We use the following strategy for dealing with Ctrl+C/KeyboardInterrupt in
30    # subprocesses created by the multiprocessing.Pool.
31    # https://noswap.com/blog/python-multiprocessing-keyboardinterrupt
32    signal.signal(signal.SIGINT, signal.SIG_IGN)
33
34
35def execute(test):
36    """Run one test in a multiprocessing.Pool
37
38    Side effects in this function and functions it calls are not visible in the
39    main lit process.
40
41    Arguments and results of this function are pickled, so they should be cheap
42    to copy.
43    """
44    with _get_parallelism_semaphore(test):
45        result = _execute(test, _lit_config)
46
47    test.setResult(result)
48    return test
49
50
51# TODO(python3): replace with contextlib.nullcontext
52@contextlib.contextmanager
53def NopSemaphore():
54    yield
55
56
57def _get_parallelism_semaphore(test):
58    pg = test.config.parallelism_group
59    if callable(pg):
60        pg = pg(test)
61    return _parallelism_semaphores.get(pg, NopSemaphore())
62
63
64# Do not inline! Directly used by LitTestCase.py
65def _execute(test, lit_config):
66    start = time.time()
67    result = _execute_test_handle_errors(test, lit_config)
68    result.elapsed = time.time() - start
69    result.start = start
70    result.pid = os.getpid()
71    return result
72
73
74def _execute_test_handle_errors(test, lit_config):
75    try:
76        result = test.config.test_format.execute(test, lit_config)
77        return _adapt_result(result)
78    except:
79        if lit_config.debug:
80            raise
81        output = 'Exception during script execution:\n'
82        output += traceback.format_exc()
83        output += '\n'
84        return lit.Test.Result(lit.Test.UNRESOLVED, output)
85
86
87# Support deprecated result from execute() which returned the result
88# code and additional output as a tuple.
89def _adapt_result(result):
90    if isinstance(result, lit.Test.Result):
91        return result
92    assert isinstance(result, tuple)
93    code, output = result
94    return lit.Test.Result(code, output)
95