Skip to content

Commit

Permalink
Get rid of possible deadlock
Browse files Browse the repository at this point in the history
Previously it could theoretically happen that loop() would set
self.rate_ok to False and notify() self.tick, without ticker() waiting
on self.tick. This would mean a deadlock.
  • Loading branch information
antonijn committed Jan 27, 2024
1 parent 3d138a8 commit 134d66c
Showing 1 changed file with 19 additions and 2 deletions.
21 changes: 19 additions & 2 deletions rate_limit.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,32 @@ def __init__(self, schema, client, interval):
self.telegram = None
self.msg = Condition()
self.tick = Condition()
self.started = Condition()
self.total_msgs = 0
self.epoch = time.monotonic_ns()
self.rate_ok = True

logging.debug(f'Rate limiter epoch: {self.epoch} ns')

Thread(target=self.ticker, daemon=True).start()
Thread(target=self.loop, daemon=True).start()
# Make sure we return an object with a consistent state. msg and
# tick conditions must be in locked or waiting state before
# we start receiving messages
with self.started:
Thread(target=self.ticker, daemon=True).start()
self.started.wait()
Thread(target=self.loop, daemon=True).start()
self.started.wait()

def next_ts(self):
return self.epoch + self.interval_ns * self.total_msgs

def ticker(self):
with self.tick:
# Notify parent thread that we're holding the tick lock, and
# it's okay to continue
with self.started:
self.started.notify()

while True:
self.tick.wait()
sleep_ns = self.next_ts() - time.monotonic_ns()
Expand All @@ -63,6 +75,11 @@ def ticker(self):

def loop(self):
with self.msg:
# Notify parent thread that we're holding the msg lock, and
# it's okay to continue
with self.started:
self.started.notify()

while True:
self.msg.wait()
if not self.rate_ok:
Expand Down

0 comments on commit 134d66c

Please sign in to comment.