From 8821f989103d592458b655580c08d3b2993f1fab Mon Sep 17 00:00:00 2001 From: Mike McLean Date: Apr 11 2016 16:09:28 +0000 Subject: [PATCH 1/2] add unit test for using profiles in threads see: https://pagure.io/koji/issue/58 --- diff --git a/hub/__init__.py b/hub/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/hub/__init__.py diff --git a/tests/test_profiles.py b/tests/test_profiles.py new file mode 100644 index 0000000..5e551a4 --- /dev/null +++ b/tests/test_profiles.py @@ -0,0 +1,41 @@ +import unittest + +import koji +import sys +import threading +import traceback + + +class ProfilesTestCase(unittest.TestCase): + + def test_profile_threading(self): + """ Test that profiles thread safe""" + # see: https://pagure.io/koji/issue/58 and https://pagure.io/pungi/issue/253 + # loop a few times to increase chances of hitting race conditions + for i in range(20): + errors = {} + threads = [threading.Thread(target=stress, args=(errors, _)) for _ in xrange(100)] + for t in threads: + t.start() + for t in threads: + t.join(30) + for n in errors: + err = errors[n] + if err is not None: + print err + assert False + + +def stress(errors, n): + errors[n] = "Failed to start" + try: + koji.get_profile_module('koji') + except Exception: + # if we don't catch this, nose seems to ignore the test + errors[n] = ''.join(traceback.format_exception(*sys.exc_info())) + return + else: + errors[n] = None + + + From 014e9aee4bc6924509d0f4af04c0e2b274559ccb Mon Sep 17 00:00:00 2001 From: Mike McLean Date: Apr 11 2016 16:09:29 +0000 Subject: [PATCH 2/2] acquire import lock in get_profile_module see: https://pagure.io/koji/issue/58 --- diff --git a/koji/__init__.py b/koji/__init__.py index cf08a45..de1c676 100644 --- a/koji/__init__.py +++ b/koji/__init__.py @@ -1578,24 +1578,30 @@ def get_profile_module(profile_name, config=None): # Prepare module name mod_name = "__%s__%s" % (__name__, profile_name) - # Check if profile module exists and if so return it - if mod_name in PROFILE_MODULES: - return PROFILE_MODULES[mod_name] - - # Load current module under a new name - koji_module_loc = imp.find_module(__name__) - mod = imp.load_module(mod_name, - None, - koji_module_loc[1], - koji_module_loc[2]) - - # Tweak config of the new module - mod.config = config - mod.BASEDIR = config.topdir - mod.pathinfo.topdir = config.topdir - - # Be sure that get_profile_module is only called from main module - mod.get_profile_module = get_profile_module + imp.acquire_lock() + try: + # Check if profile module exists and if so return it + if mod_name in PROFILE_MODULES: + return PROFILE_MODULES[mod_name] + + # Load current module under a new name + koji_module_loc = imp.find_module(__name__) + mod = imp.load_module(mod_name, + None, + koji_module_loc[1], + koji_module_loc[2]) + + # Tweak config of the new module + mod.config = config + mod.BASEDIR = config.topdir + mod.pathinfo.topdir = config.topdir + + # Be sure that get_profile_module is only called from main module + mod.get_profile_module = get_profile_module + + PROFILE_MODULES[mod_name] = mod + finally: + imp.release_lock() return mod