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: Return a list of worker_address, has n_cpu vacant CPUs and n_gpu vacant GPUs at least.
  
  If there is no vacant CPU and GPU in the cluster, this will return empty list.
  
  Args:
      n_cpu (int): request a Job with n_cpu CPUs
      n_gpu (int): request a Job with n_gpu GPUs
  
  Return:
      An ``InitializedJob`` that has information about available computation resources.
docstr_tok: ["Return","a","list","of","worker_address",",","has","n_cpu","vacant","CPUs","and","n_gpu","vacant","GPUs","at","least",".","If","there","is","no","vacant","CPU","and","GPU","in","the","cluster",",","this","will","return","empty","list",".","Args",":","n_cpu","(","int",")",":","request","a","Job","with","n_cpu","CPUs","n_gpu","(","int",")",":","request","a","Job","with","n_gpu","GPUs","Return",":","An","``","InitializedJob","``","that","has","information","about","available","computation","resources","."]
code: def filter(self, n_cpu, n_gpu):
      """Return a list of worker_address, has n_cpu vacant CPUs and n_gpu vacant GPUs at least.
  
      If there is no vacant CPU and GPU in the cluster, this will return empty list.
  
      Args:
          n_cpu (int): request a Job with n_cpu CPUs
          n_gpu (int): request a Job with n_gpu GPUs
  
      Return:
          An ``InitializedJob`` that has information about available computation resources.
      """
      candidates = set(self.worker_vacant_jobs.keys())
      if n_gpu > 0:
          candidates = candidates.intersection(
              set(self.gpu_resource.filter(n_gpu))
          )
      if n_cpu > 0:
          candidates = candidates.intersection(
              set(self.cpu_resource.filter(n_cpu))
          )
      return list(candidates)
  
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 9 for calling the WorkerManager obj's underlying member method code with example usage: obj.filter(n_cpu, n_gpu) and returns: list