Wait for threads to terminate. This call blocks until a set of threads have terminated. The set of thread is the union of the threads passed in the `threads` argument and the list of threads that registered with the coordinator by calling `Coordinator.register_thread()`. After
(self, threads=None, stop_grace_period_secs=120,
ignore_live_threads=False)
| 320 | self._registered_threads.add(thread) |
| 321 | |
| 322 | def join(self, threads=None, stop_grace_period_secs=120, |
| 323 | ignore_live_threads=False): |
| 324 | """Wait for threads to terminate. |
| 325 | |
| 326 | This call blocks until a set of threads have terminated. The set of thread |
| 327 | is the union of the threads passed in the `threads` argument and the list |
| 328 | of threads that registered with the coordinator by calling |
| 329 | `Coordinator.register_thread()`. |
| 330 | |
| 331 | After the threads stop, if an `exc_info` was passed to `request_stop`, that |
| 332 | exception is re-raised. |
| 333 | |
| 334 | Grace period handling: When `request_stop()` is called, threads are given |
| 335 | 'stop_grace_period_secs' seconds to terminate. If any of them is still |
| 336 | alive after that period expires, a `RuntimeError` is raised. Note that if |
| 337 | an `exc_info` was passed to `request_stop()` then it is raised instead of |
| 338 | that `RuntimeError`. |
| 339 | |
| 340 | Args: |
| 341 | threads: List of `threading.Threads`. The started threads to join in |
| 342 | addition to the registered threads. |
| 343 | stop_grace_period_secs: Number of seconds given to threads to stop after |
| 344 | `request_stop()` has been called. |
| 345 | ignore_live_threads: If `False`, raises an error if any of the threads are |
| 346 | still alive after `stop_grace_period_secs`. |
| 347 | |
| 348 | Raises: |
| 349 | RuntimeError: If any thread is still alive after `request_stop()` |
| 350 | is called and the grace period expires. |
| 351 | """ |
| 352 | # Threads registered after this call will not be joined. |
| 353 | with self._lock: |
| 354 | if threads is None: |
| 355 | threads = self._registered_threads |
| 356 | else: |
| 357 | threads = self._registered_threads.union(set(threads)) |
| 358 | # Copy the set into a list to avoid race conditions where a new thread |
| 359 | # is added while we are waiting. |
| 360 | threads = list(threads) |
| 361 | |
| 362 | # Wait for all threads to stop or for request_stop() to be called. |
| 363 | while any(t.is_alive() for t in threads) and not self.wait_for_stop(1.0): |
| 364 | pass |
| 365 | |
| 366 | # If any thread is still alive, wait for the grace period to expire. |
| 367 | # By the time this check is executed, threads may still be shutting down, |
| 368 | # so we add a sleep of increasing duration to give them a chance to shut |
| 369 | # down without losing too many cycles. |
| 370 | # The sleep duration is limited to the remaining grace duration. |
| 371 | stop_wait_secs = 0.001 |
| 372 | while any(t.is_alive() for t in threads) and stop_grace_period_secs >= 0.0: |
| 373 | time.sleep(stop_wait_secs) |
| 374 | stop_grace_period_secs -= stop_wait_secs |
| 375 | stop_wait_secs = 2 * stop_wait_secs |
| 376 | # Keep the waiting period within sane bounds. |
| 377 | # The minimum value is to avoid decreasing stop_wait_secs to a value |
| 378 | # that could cause stop_grace_period_secs to remain unchanged. |
| 379 | stop_wait_secs = max(min(stop_wait_secs, stop_grace_period_secs), 0.001) |