Skip to content

Commit 54044db

Browse files
jdDavid Bouchare
authored andcommitted
Fix periodic timer error on interpreter shutdown (DataDog#423)
The current hack does not work, as I'm still able to see such messages in the log: Exception in thread Thread-1 (most likely raised during interpreter shutdown): Traceback (most recent call last): File "pyenv/versions/2.7.12/lib/python2.7/threading.py", line 801, in __bootstrap_inner File "python/lib/python2.7/site-packages/datadog/threadstats/periodic_timer.py", line 43, in run File "pyenv/versions/2.7.12/lib/python2.7/threading.py", line 614, in wait File "pyenv/versions/2.7.12/lib/python2.7/threading.py", line 355, in wait <type 'exceptions.TypeError'>: 'NoneType' object is not callable This changes the current code to leverage the same hack that the `threading` library uses: if the `sys` module is None, there's a good chance that's because the interpreter is shutdown down. This change also improves the waiting loop by using the return value from `Event.wait()` which returns the value of the flag. That makes the thread faster to quit when `end()` is called.
1 parent a8d3bff commit 54044db

File tree

1 file changed

+10
-21
lines changed

1 file changed

+10
-21
lines changed

datadog/threadstats/periodic_timer.py

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55

66
from threading import Thread, Event
7+
import sys
78

89

910
class PeriodicTimer(Thread):
@@ -18,27 +19,15 @@ def __init__(self, interval, function, *args, **kwargs):
1819
self.kwargs = kwargs
1920
self.finished = Event()
2021

21-
def _is_alive(self):
22-
# HACK: The Python interpreter can start cleaning up objects in the
23-
# main thread before killing daemon threads, so these references can be
24-
# null result in errors like that in case #18, tagged
25-
# "most likely raised during interpreter shutdown". This is hack to
26-
# try gracefully fail in these circumstances.
27-
#
28-
# http://stackoverflow.com/questions/1745232
29-
return (
30-
bool(self.finished) and
31-
bool(self.interval) and
32-
bool(self.function)
33-
)
34-
3522
def end(self):
36-
if self._is_alive():
37-
self.finished.set()
23+
self.finished.set()
3824

3925
def run(self):
40-
while True:
41-
if not self._is_alive() or self.finished.isSet():
42-
break
43-
self.finished.wait(self.interval)
44-
self.function(*self.args, **self.kwargs)
26+
while not self.finished.wait(self.interval):
27+
try:
28+
self.function(*self.args, **self.kwargs)
29+
except Exception:
30+
# If `sys` is None, it means the interpreter is shutting down
31+
# and it's very likely the reason why we got an exception.
32+
if sys is not None:
33+
raise

0 commit comments

Comments
 (0)