diff --git a/loopy/kernel/tools.py b/loopy/kernel/tools.py index 15db06ad78b448675b193a2d880ae5b50073e99d..f2104ca76f3e5feff1eb84eae9a2420526b58258 100644 --- a/loopy/kernel/tools.py +++ b/loopy/kernel/tools.py @@ -1391,7 +1391,7 @@ def draw_dependencies_as_unicode_arrows( if value is None: del columns_in_use[col] - # }} + # }}} processed_ids.add(insn.id) diff --git a/loopy/schedule/__init__.py b/loopy/schedule/__init__.py index b196b343edebf0b9346b449bc5a44bcc065407a2..f40a943b0fed59f2a7756b2a7688a8ce48cb5b5f 100644 --- a/loopy/schedule/__init__.py +++ b/loopy/schedule/__init__.py @@ -29,6 +29,8 @@ import sys import islpy as isl from loopy.diagnostic import warn_with_kernel, LoopyError # noqa +from pytools import MinRecursionLimit + from pytools.persistent_dict import WriteOncePersistentDict from loopy.tools import LoopyKeyBuilder from loopy.version import DATA_MODEL_VERSION @@ -1810,12 +1812,27 @@ def insert_barriers(kernel, schedule, synchronization_kind, verify_only, level=0 # }}} +class MinRecursionLimitForScheduling(MinRecursionLimit): + def __init__(self, kernel): + MinRecursionLimit.__init__(self, + len(kernel.instructions) * 2 + len(kernel.all_inames()) * 4) + + # {{{ main scheduling entrypoint def generate_loop_schedules(kernel, debug_args={}): - from pytools import MinRecursionLimit - with MinRecursionLimit(max(len(kernel.instructions) * 2, - len(kernel.all_inames()) * 4)): + """ + .. warning:: + + This function needs to be called inside (another layer) of a + :class:`MinRecursionLimitForScheduling` context manager, and the + context manager needs to end *after* the last reference to the + generators has gone out of scope. Otherwise, the high-recursion-limit + generator chain may not be successfully garbage-collected and cause an + internal error in the Python runtime. + """ + + with MinRecursionLimitForScheduling(kernel): for sched in generate_loop_schedules_inner(kernel, debug_args=debug_args): yield sched @@ -2003,6 +2020,19 @@ schedule_cache = WriteOncePersistentDict( key_builder=LoopyKeyBuilder()) +def _get_one_scheduled_kernel_inner(kernel): + # This helper function exists to ensure that the generator chain is fully + # out of scope after the function returns. This allows it to be + # garbage-collected in the exit handler of the + # MinRecursionLimitForScheduling context manager in the surrounding + # function, because it possilby cannot be safely collected with a lower + # recursion limit without crashing the Python runtime. + # + # See https://gitlab.tiker.net/inducer/sumpy/issues/31 for context. + + return next(iter(generate_loop_schedules(kernel))) + + def get_one_scheduled_kernel(kernel): from loopy import CACHING_ENABLED @@ -2024,7 +2054,8 @@ def get_one_scheduled_kernel(kernel): logger.info("%s: schedule start" % kernel.name) - result = next(iter(generate_loop_schedules(kernel))) + with MinRecursionLimitForScheduling(kernel): + result = _get_one_scheduled_kernel_inner(kernel) logger.info("%s: scheduling done after %.2f s" % ( kernel.name, time()-start_time)) diff --git a/setup.py b/setup.py index b8f36d12559f05a47ef57dd06efd4761e3b3ad9a..bd94ea7e7e387709684adbc43a5753f1395df2f7 100644 --- a/setup.py +++ b/setup.py @@ -37,7 +37,7 @@ setup(name="loo.py", ], install_requires=[ - "pytools>=2017.6", + "pytools>=2018.1", "pymbolic>=2016.2", "genpy>=2016.1.2", "cgen>=2016.1",