95 lines
2.6 KiB
Python
95 lines
2.6 KiB
Python
"""
|
|
The functions in this module are meant to run on a separate worker process.
|
|
Exception: in single process mode _execute is called directly.
|
|
|
|
For efficiency, we copy all data needed to execute all tests into each worker
|
|
and store it in global variables. This reduces the cost of each task.
|
|
"""
|
|
import contextlib
|
|
import os
|
|
import signal
|
|
import time
|
|
import traceback
|
|
|
|
import lit.Test
|
|
import lit.util
|
|
|
|
|
|
_lit_config = None
|
|
_parallelism_semaphores = None
|
|
|
|
|
|
def initialize(lit_config, parallelism_semaphores):
|
|
"""Copy data shared by all test executions into worker processes"""
|
|
global _lit_config
|
|
global _parallelism_semaphores
|
|
_lit_config = lit_config
|
|
_parallelism_semaphores = parallelism_semaphores
|
|
|
|
# We use the following strategy for dealing with Ctrl+C/KeyboardInterrupt in
|
|
# subprocesses created by the multiprocessing.Pool.
|
|
# https://noswap.com/blog/python-multiprocessing-keyboardinterrupt
|
|
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
|
|
|
|
|
def execute(test):
|
|
"""Run one test in a multiprocessing.Pool
|
|
|
|
Side effects in this function and functions it calls are not visible in the
|
|
main lit process.
|
|
|
|
Arguments and results of this function are pickled, so they should be cheap
|
|
to copy.
|
|
"""
|
|
with _get_parallelism_semaphore(test):
|
|
result = _execute(test, _lit_config)
|
|
|
|
test.setResult(result)
|
|
return test
|
|
|
|
|
|
# TODO(python3): replace with contextlib.nullcontext
|
|
@contextlib.contextmanager
|
|
def NopSemaphore():
|
|
yield
|
|
|
|
|
|
def _get_parallelism_semaphore(test):
|
|
pg = test.config.parallelism_group
|
|
if callable(pg):
|
|
pg = pg(test)
|
|
return _parallelism_semaphores.get(pg, NopSemaphore())
|
|
|
|
|
|
# Do not inline! Directly used by LitTestCase.py
|
|
def _execute(test, lit_config):
|
|
start = time.time()
|
|
result = _execute_test_handle_errors(test, lit_config)
|
|
result.elapsed = time.time() - start
|
|
result.start = start
|
|
result.pid = os.getpid()
|
|
return result
|
|
|
|
|
|
def _execute_test_handle_errors(test, lit_config):
|
|
try:
|
|
result = test.config.test_format.execute(test, lit_config)
|
|
return _adapt_result(result)
|
|
except:
|
|
if lit_config.debug:
|
|
raise
|
|
output = "Exception during script execution:\n"
|
|
output += traceback.format_exc()
|
|
output += "\n"
|
|
return lit.Test.Result(lit.Test.UNRESOLVED, output)
|
|
|
|
|
|
# Support deprecated result from execute() which returned the result
|
|
# code and additional output as a tuple.
|
|
def _adapt_result(result):
|
|
if isinstance(result, lit.Test.Result):
|
|
return result
|
|
assert isinstance(result, tuple)
|
|
code, output = result
|
|
return lit.Test.Result(code, output)
|