class_docstr: Coordinator is responsible for running user code on a cluster.
  
  Has one main method, `launch_clients`, which runs the user's code
  on every node of the cluster. Since all we pass to each worker are
  their worker id and the distribution strategy, this means that each
  worker will do its own full graph transformation based on the strategy.
  Then the workers will sync up when they run the graphs.
  
  `join` is called by `atexit` so that we can try to make sure the
  remote processes are killed with the chief's process is ended.
docstr: Wait for all subprocesses of remote workers to be completed.
code: def join(self):
      """Wait for all subprocesses of remote workers to be completed."""
      logging.debug("Joining workers...")
      for t in self.threads:
          t.join()
  
file_path: autodist/autodist/coordinator.py
imports: import sys
  import threading
  import atexit
  import os
  from autodist.const import ENV, DEFAULT_SERIALIZATION_DIR
  from autodist.resource_spec import DeviceSpec
  from autodist.utils import logging
label_desc: Use image node_id 3 for calling the Coordinator obj's underlying member method code with example usage: obj.join() without return types