class_docstr: A coordinator for threads.
  
  This class implements a simple mechanism to coordinate the termination of a
  set of threads.
  
  #### Usage:
  
  ```python
  # Create a coordinator.
  coord = Coordinator()
  # Start a number of threads, passing the coordinator to each of them.
  ...start thread 1...(coord, ...)
  ...start thread N...(coord, ...)
  # Wait for all the threads to terminate.
  coord.join(threads)
  ```
  
  Any of the threads can call `coord.request_stop()` to ask for all the threads
  to stop.  To cooperate with the requests, each thread must check for
  `coord.should_stop()` on a regular basis.  `coord.should_stop()` returns
  `True` as soon as `coord.request_stop()` has been called.
  
  A typical thread running with a coordinator will do something like:
  
  ```python
  while not coord.should_stop():
    ...do some work...
  ```
  
  #### Exception handling:
  
  A thread can report an exception to the coordinator as part of the
  `request_stop()` call.  The exception will be re-raised from the
  `coord.join()` call.
  
  Thread code:
  
  ```python
  try:
    while not coord.should_stop():
      ...do some work...
  except Exception as e:
    coord.request_stop(e)
  ```
  
  Main code:
  
  ```python
  try:
    ...
    coord = Coordinator()
    # Start a number of threads, passing the coordinator to each of them.
    ...start thread 1...(coord, ...)
    ...start thread N...(coord, ...)
    # Wait for all the threads to terminate.
    coord.join(threads)
  except Exception as e:
    ...exception that was passed to coord.request_stop()
  ```
  
  To simplify the thread implementation, the Coordinator provides a
  context handler `stop_on_exception()` that automatically requests a stop if
  an exception is raised.  Using the context handler the thread code above
  can be written as:
  
  ```python
  with coord.stop_on_exception():
    while not coord.should_stop():
      ...do some work...
  ```
  
  #### Grace period for stopping:
  
  After a thread has called `coord.request_stop()` the other threads have a
  fixed time to stop, this is called the 'stop grace period' and defaults to 2
  minutes.  If any of the threads is still alive after the grace period expires
  `coord.join()` raises a RuntimeError reporting the laggards.
  
  ```python
  try:
    ...
    coord = Coordinator()
    # Start a number of threads, passing the coordinator to each of them.
    ...start thread 1...(coord, ...)
    ...start thread N...(coord, ...)
    # Wait for all the threads to terminate, give them 10s grace period
    coord.join(threads, stop_grace_period_secs=10)
  except RuntimeError:
    ...one of the threads took more than 10s to stop after request_stop()
    ...was called.
  except Exception:
    ...exception that was passed to coord.request_stop()
  ```
class_docstr_tok: ["A","coordinator","for","threads",".","This","class","implements","a","simple","mechanism","to","coordinate","the","termination","of","a","set","of","threads",".","#","#","#","#","Usage",":","``","`","python","#","Create","a","coordinator",".","coord","=","Coordinator","(",")","#","Start","a","number","of","threads",",","passing","the","coordinator","to","each","of","them",".","...","start","thread","1","...","(","coord",",","...",")","...","start","thread","N","...","(","coord",",","...",")","#","Wait","for","all","the","threads","to","terminate",".","coord.join","(","threads",")","``","`","Any","of","the","threads","can","call","`","coord.request_stop","(",")","`","to","ask","for","all","the","threads","to","stop",".","To","cooperate","with","the","requests",",","each","thread","must","check","for","`","coord.should_stop","(",")","`","on","a","regular","basis",".","`","coord.should_stop","(",")","`","returns","`","True","`","as","soon","as","`","coord.request_stop","(",")","`","has","been","called",".","A","typical","thread","running","with","a","coordinator","will","do","something","like",":","``","`","python","while","not","coord.should_stop","(",")",":","...","do","some","work","...","``","`","#","#","#","#","Exception","handling",":","A","thread","can","report","an","exception","to","the","coordinator","as","part","of","the","`","request_stop","(",")","`","call",".","The","exception","will","be","re-raised","from","the","`","coord.join","(",")","`","call",".","Thread","code",":","``","`","python","try",":","while","not","coord.should_stop","(",")",":","...","do","some","work","...","except","Exception","as","e",":","coord.request_stop","(","e",")","``","`","Main","code",":","``","`","python","try",":","...","coord","=","Coordinator","(",")","#","Start","a","number","of","threads",",","passing","the","coordinator","to","each","of","them",".","...","start","thread","1","...","(","coord",",","...",")","...","start","thread","N","...","(","coord",",","...",")","#","Wait","for","all","the","threads","to","terminate",".","coord.join","(","threads",")","except","Exception","as","e",":","...","exception","that","was","passed","to","coord.request_stop","(",")","``","`","To","simplify","the","thread","implementation",",","the","Coordinator","provides","a","context","handler","`","stop_on_exception","(",")","`","that","automatically","requests","a","stop","if","an","exception","is","raised",".","Using","the","context","handler","the","thread","code","above","can","be","written","as",":","``","`","python","with","coord.stop_on_exception","(",")",":","while","not","coord.should_stop","(",")",":","...","do","some","work","...","``","`","#","#","#","#","Grace","period","for","stopping",":","After","a","thread","has","called","`","coord.request_stop","(",")","`","the","other","threads","have","a","fixed","time","to","stop",",","this","is","called","the","'stop","grace","period","'","and","defaults","to","2","minutes",".","If","any","of","the","threads","is","still","alive","after","the","grace","period","expires","`","coord.join","(",")","`","raises","a","RuntimeError","reporting","the","laggards",".","``","`","python","try",":","...","coord","=","Coordinator","(",")","#","Start","a","number","of","threads",",","passing","the","coordinator","to","each","of","them",".","...","start","thread","1","...","(","coord",",","...",")","...","start","thread","N","...","(","coord",",","...",")","#","Wait","for","all","the","threads","to","terminate",",","give","them","10s","grace","period","coord.join","(","threads",",","stop_grace_period_secs=10",")","except","RuntimeError",":","...","one","of","the","threads","took","more","than","10s","to","stop","after","request_stop","(",")","...","was","called",".","except","Exception",":","...","exception","that","was","passed","to","coord.request_stop","(",")","``","`"]
docstr: 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 the threads stop, if an `exc_info` was passed to `request_stop`, that
  exception is re-raised.
  
  Grace period handling: When `request_stop()` is called, threads are given
  'stop_grace_period_secs' seconds to terminate.  If any of them is still
  alive after that period expires, a `RuntimeError` is raised.  Note that if
  an `exc_info` was passed to `request_stop()` then it is raised instead of
  that `RuntimeError`.
  
  Args:
    threads: List of `threading.Threads`. The started threads to join in
      addition to the registered threads.
    stop_grace_period_secs: Number of seconds given to threads to stop after
      `request_stop()` has been called.
    ignore_live_threads: If `False`, raises an error if any of the threads are
      still alive after `stop_grace_period_secs`.
  
  Raises:
    RuntimeError: If any thread is still alive after `request_stop()`
      is called and the grace period expires.
docstr_tok: ["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","the","threads","stop",",","if","an","`","exc_info","`","was","passed","to","`","request_stop","`",",","that","exception","is","re-raised",".","Grace","period","handling",":","When","`","request_stop","(",")","`","is","called",",","threads","are","given","'stop_grace_period_secs","'","seconds","to","terminate",".","If","any","of","them","is","still","alive","after","that","period","expires",",","a","`","RuntimeError","`","is","raised",".","Note","that","if","an","`","exc_info","`","was","passed","to","`","request_stop","(",")","`","then","it","is","raised","instead","of","that","`","RuntimeError","`",".","Args",":","threads",":","List","of","`","threading.Threads","`",".","The","started","threads","to","join","in","addition","to","the","registered","threads",".","stop_grace_period_secs",":","Number","of","seconds","given","to","threads","to","stop","after","`","request_stop","(",")","`","has","been","called",".","ignore_live_threads",":","If","`","False","`",",","raises","an","error","if","any","of","the","threads","are","still","alive","after","`","stop_grace_period_secs","`",".","Raises",":","RuntimeError",":","If","any","thread","is","still","alive","after","`","request_stop","(",")","`","is","called","and","the","grace","period","expires","."]
code: def join(
      self,
      threads=None,
      stop_grace_period_secs=120,
      ignore_live_threads=False,
  ):
      """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 the threads stop, if an `exc_info` was passed to `request_stop`, that
      exception is re-raised.
  
      Grace period handling: When `request_stop()` is called, threads are given
      'stop_grace_period_secs' seconds to terminate.  If any of them is still
      alive after that period expires, a `RuntimeError` is raised.  Note that if
      an `exc_info` was passed to `request_stop()` then it is raised instead of
      that `RuntimeError`.
  
      Args:
        threads: List of `threading.Threads`. The started threads to join in
          addition to the registered threads.
        stop_grace_period_secs: Number of seconds given to threads to stop after
          `request_stop()` has been called.
        ignore_live_threads: If `False`, raises an error if any of the threads are
          still alive after `stop_grace_period_secs`.
  
      Raises:
        RuntimeError: If any thread is still alive after `request_stop()`
          is called and the grace period expires.
      """
      # Threads registered after this call will not be joined.
      with self._lock:
          if threads is None:
              threads = self._registered_threads
          else:
              threads = self._registered_threads.union(set(threads))
          # Copy the set into a list to avoid race conditions where a new thread
          # is added while we are waiting.
          threads = list(threads)
  
      # Wait for all threads to stop or for request_stop() to be called.
      while any(
          t.is_alive() for t in threads
      ) and not self.wait_for_stop(1.0):
          pass
  
      # If any thread is still alive, wait for the grace period to expire.
      # By the time this check is executed, threads may still be shutting down,
      # so we add a sleep of increasing duration to give them a chance to shut
      # down without losing too many cycles.
      # The sleep duration is limited to the remaining grace duration.
      stop_wait_secs = 0.001
      while (
          any(t.is_alive() for t in threads)
          and stop_grace_period_secs >= 0.0
      ):
          time.sleep(stop_wait_secs)
          stop_grace_period_secs -= stop_wait_secs
          stop_wait_secs = 2 * stop_wait_secs
          # Keep the waiting period within sane bounds.
          # The minimum value is to avoid decreasing stop_wait_secs to a value
          # that could cause stop_grace_period_secs to remain unchanged.
          stop_wait_secs = max(
              min(stop_wait_secs, stop_grace_period_secs), 0.001
          )
  
      # List the threads still alive after the grace period.
      stragglers = [t.name for t in threads if t.is_alive()]
  
      # Terminate with an exception if appropriate.
      with self._lock:
          self._joined = True
          self._registered_threads = set()
          if self._exc_info_to_raise:
              _, ex_instance, _ = self._exc_info_to_raise
              raise ex_instance
          elif stragglers:
              if ignore_live_threads:
                  logging.info(
                      "Coordinator stopped with threads still running: %s",
                      " ".join(stragglers),
                  )
              else:
                  raise RuntimeError(
                      "Coordinator stopped with threads still running: %s"
                      % " ".join(stragglers)
                  )
  
imports: import contextlib
  import sys
  import threading
  import time
  from tensorflow.python.framework import errors
  from tensorflow.python.platform import tf_logging
  from tensorflow.python.util import compat
  from tensorflow.python.util.tf_export import tf_export
label_desc: Use image node_id 9 for calling the Coordinator obj's underlying member method code with example usage: obj.join(threads, stop_grace_period_secs, ignore_live_threads) without return types