i3-companion: rework dampener to be easier to understand

We have a worker running as long as there is work. The optional sleep
is not implemented. There is a slight semantic difference: the work is
not postponed indefinitely.
This commit is contained in:
Vincent Bernat 2021-07-12 23:55:50 +02:00
parent 9b0bb1ce76
commit f56b995d5a

View file

@ -105,16 +105,17 @@ def dampen(sleep, *, unless=None, retry=0):
""" """
def decorator(fn): def decorator(fn):
async def fn_now(me, retry, *args, **kwargs): async def worker():
if unless is None or not unless(*args, **kwargs): while True:
await asyncio.sleep(sleep) await asyncio.sleep(sleep)
me["sleeping"] = False
# From here, we do not expect to be cancelled. Ensure only retry, args, kwargs = fn.queue
# one of us is running. fn.queue = None
async with fn.lock:
# Execute the work
logger.debug(f"execute work for {fn}")
try: try:
return await fn(*args, **kwargs) await fn(*args, **kwargs)
except Exception as e: except Exception as e:
if not retry: if not retry:
logger.exception(f"while executing {fn}: %s", e) logger.exception(f"while executing {fn}: %s", e)
@ -125,32 +126,29 @@ def dampen(sleep, *, unless=None, retry=0):
retry, retry,
str(e), str(e),
) )
# Run again, unless we have something already scheduled
if fn.last_task["sleeping"]: # Retry, unless we have something already scheduled
if fn.queue is not None:
return return
fn.last_task = dict(sleeping=True) fn.queue = (retry, args, kwargs)
fn.last_task["task"] = asyncio.create_task( # Do we still have something to do?
fn_now(fn.last_task, retry, *args, **kwargs) if fn.queue is None:
) break
# No more work
logger.debug(f"no more work for {fn}")
fn.worker = None
@functools.wraps(fn) @functools.wraps(fn)
async def wrapper(*args, **kwargs): async def wrapper(*args, **kwargs):
# Initialize a lock (we need an active loop for that) fn.queue = (retry, args, kwargs)
if fn.lock is None: if fn.worker is None:
fn.lock = asyncio.Lock() logger.debug(f"create new worker for {fn}")
fn.worker = asyncio.create_task(worker())
else:
logger.debug(f"enqueue new work for {fn}")
# If possible, cancel last task if it's sleeping fn.worker = None
if fn.last_task is not None and fn.last_task["sleeping"]:
logger.debug(f"cancel call to {fn}")
fn.last_task["task"].cancel()
logger.debug(f"dampening call to {fn}")
fn.last_task = dict(sleeping=True)
fn.last_task["task"] = asyncio.create_task(
fn_now(fn.last_task, retry, *args, **kwargs)
)
fn.last_task = None
fn.lock = None
return wrapper return wrapper
return decorator return decorator