From fb20759b77edfb1f0ce4b77f1a66589139443ce6 Mon Sep 17 00:00:00 2001 From: Vikentiy Fesunov Date: Thu, 15 Aug 2024 16:39:47 +0000 Subject: [PATCH] Avoid deadlock in getaddrinfo getaddrinfo may use an internal lock that, in case of a concurrent fork, may be left in locked state and cause child process to deadlock. --- datadog/dogstatsd/base.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/datadog/dogstatsd/base.py b/datadog/dogstatsd/base.py index 72b3a7901..8b503b6b7 100644 --- a/datadog/dogstatsd/base.py +++ b/datadog/dogstatsd/base.py @@ -1531,8 +1531,15 @@ def pre_fork(self): self._stop_flush_thread() self._stop_sender_thread() + # Prevent concurrent calls to system libraries (notably + # getaddrinfo) which may leave internal locks in a locked + # state and deadlock the child. + self._socket_lock.acquire() + def post_fork_parent(self): """Restore the client state after a fork in the parent process.""" + self._socket_lock.release() + if self._disable_aggregating: self._start_flush_thread( self._flush_interval, @@ -1548,21 +1555,17 @@ def post_fork_parent(self): def post_fork_child(self): """Restore the client state after a fork in the child process.""" + self._socket_lock.release() self._config_lock.release() # Discard the locks that could have been locked at the time # when we forked. This may cause inconsistent internal state, # which we will fix in the next steps. - self._socket_lock = Lock() self._buffer_lock = RLock() # Reset the buffer so we don't send metrics from the parent # process. Also makes sure buffer properties are consistent. self._reset_buffer() - # Execute the socket_path setter to reconcile transport and - # payload size properties in respect to socket_path value. - self.socket_path = self.socket_path - self.close_socket() with self._config_lock: if self._disable_aggregating: