class_docstr: A thread-safe data structure used to respond to resource request,
     maintaining the available computation resources in each worker.
  
  Attributes:
      worker_hostname (dict): A dict to record worker hostname.
      worker_vacant_jobs (dict): Record which vacant jobs does each
                                 worker has.
      worker_used_jobs (dict): Record which used jobs does each
                                 worker has.
      master_ip (str): IP address of the master node.
class_docstr_tok: ["A","thread-safe","data","structure","used","to","respond","to","resource","request",",","maintaining","the","available","computation","resources","in","each","worker",".","Attributes",":","worker_hostname","(","dict",")",":","A","dict","to","record","worker","hostname",".","worker_vacant_jobs","(","dict",")",":","Record","which","vacant","jobs","does","each","worker","has",".","worker_used_jobs","(","dict",")",":","Record","which","used","jobs","does","each","worker","has",".","master_ip","(","str",")",":","IP","address","of","the","master","node","."]
docstr: Record initialized jobs and CPU/GPU information when a worker connects.
  
  Args:
      worker (InitializedWorker): New worker with initialized jobs and CPU/GPU information.
  Return: True if succeeds.
docstr_tok: ["Record","initialized","jobs","and","CPU\/GPU","information","when","a","worker","connects",".","Args",":","worker","(","InitializedWorker",")",":","New","worker","with","initialized","jobs","and","CPU\/GPU","information",".","Return",":","True","if","succeeds","."]
code: def add_worker(self, worker):
      """Record initialized jobs and CPU/GPU information when a worker connects.
  
      Args:
          worker (InitializedWorker): New worker with initialized jobs and CPU/GPU information.
      Return: True if succeeds.
      """
      if (
          remote_constants.GPU not in self.devices
          and worker.allocated_gpu.gpu != ""
      ):
          error_message = "The CPU cluster is not allowed to accept a worker with GPU resources."
          logger.error(error_message)
          return False
      elif (
          remote_constants.CPU not in self.devices
          and worker.allocated_cpu.n_cpu > 0
      ):
          error_message = "The GPU cluster is not allowed to accept a worker with CPU resources."
          logger.error(error_message)
          return False
      with self.lock:
          for job in worker.initialized_jobs:
              self.worker_vacant_jobs[worker.worker_address][
                  job.job_address
              ] = job
          self.cpu_resource.add_cpu(
              worker.worker_address, worker.allocated_cpu
          )
          self.gpu_resource.add_gpu(
              worker.worker_address, worker.allocated_gpu
          )
  
          if (
              self.master_ip
              and worker.worker_address.split(":")[0] == self.master_ip
          ):
              self.worker_hostname[worker.worker_address] = "Master"
              self.master_ip = None
          else:
              index = 1 + len(
                  [
                      hostname
                      for hostname in self.worker_hostname.values()
                      if hostname.startswith(worker.hostname)
                  ]
              )
              self.worker_hostname[
                  worker.worker_address
              ] = "{}:{}".format(worker.hostname, index)
      return True
  
imports: import threading
  import random
  from collections import defaultdict
  from collections import namedtuple
  from parl.utils import logger
  from parl.remote import remote_constants
  from parl.remote.cpu_resource import CpuResource
  from parl.remote.gpu_resource import GpuResource
  from parl.remote.message import AllocatedCpu
  from parl.remote.message import AllocatedGpu
label_desc: Use image node_id 6 for calling the WorkerManager obj's underlying member method code with example usage: obj.add_worker(worker) and returns: True, False, False