The strategy for obtaining locks is that we first, for a given time, only try to get locks when all locks are available in order not to hold unused resources. After a certain time, however, we need to make sure that we eventually get all the locks we need. This is done by holding all locks obtained so far. In order for this strategy to work, however, also the locks of every level need to be obtained sequentially. Otherwise, if two locks at a level are requested, other processes might still starve us.
Signed-off-by: Klaus Aehlig <[email protected]> --- lib/mcpu.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/lib/mcpu.py b/lib/mcpu.py index b78b141..7e061cd 100644 --- a/lib/mcpu.py +++ b/lib/mcpu.py @@ -379,6 +379,9 @@ class Processor(object): names = _LockList(names) + # For locks of the same level, the lock order is lexicographic + names.sort() + levelname = locking.LEVEL_NAMES[level] locks = ["%s/%s" % (levelname, lock) for lock in list(names)] @@ -399,13 +402,19 @@ class Processor(object): locks = self.wconfd.Client().OpportunisticLockUnion(self._wconfdcontext, request) elif timeout is None: - self.wconfd.Client().UpdateLocksWaiting(self._wconfdcontext, priority, - request) - while True: - pending = self.wconfd.Client().HasPendingRequest(self._wconfdcontext) - if not pending: - break - time.sleep(10.0 * random.random()) + logging.info("Definitely requesting %s for %s", + request, self._wconfdcontext) + ## The only way to be sure of not getting starved is to sequentially + ## acquire the locks one by one (in lock order). + for r in request: + logging.debug("Definite request %s for %s", r, self._wconfdcontext) + self.wconfd.Client().UpdateLocksWaiting(self._wconfdcontext, priority, + [r]) + while True: + pending = self.wconfd.Client().HasPendingRequest(self._wconfdcontext) + if not pending: + break + time.sleep(10.0 * random.random()) else: logging.debug("Trying %ss to request %s for %s", timeout, request, self._wconfdcontext) -- 2.0.0.526.g5318336
