docstr: Begin handling task.
  
  Periodically check to see when enough agents are in the agent pool to start an
  instance of the task. Continue doing this until the desired number of
  conversations is had.
code: def start_task(self):
      """
      Begin handling task.
  
      Periodically check to see when enough agents are in the agent pool to start an
      instance of the task. Continue doing this until the desired number of
      conversations is had.
      """
  
      self.running = True
      while self.running:
          # Loop forever until the server is shut down
          with self.agent_pool_change_condition:
              valid_pools = self._get_unique_pool()
              for world_type, agent_pool in valid_pools.items():
                  # check if agent has exceeded max time in pool
                  world_config = self.task_configs[world_type]
                  if world_config.max_time_in_pool is not None:
                      self.check_timeout_in_pool(
                          world_type,
                          agent_pool,
                          world_config.max_time_in_pool,
                          world_config.backup_task,
                      )
  
                  needed_agents = self.max_agents_for[world_type]
                  if len(agent_pool) >= needed_agents:
                      log_utils.print_and_log(
                          logging.INFO,
                          "starting pool",
                          should_print=True,
                      )
                      # enough agents in pool to start new conversation
                      self.conversation_index += 1
                      task_id = "t_{}".format(self.conversation_index)
  
                      # Add the required number of valid agents to the conv
                      agent_states = [
                          w for w in agent_pool[:needed_agents]
                      ]
                      agents = []
                      for state in agent_states:
                          agent = self._create_agent(
                              task_id, state.get_id()
                          )
                          agent.onboard_data = state.onboard_data
                          agent.data = state.data
                          state.assign_agent_to_task(agent, task_id)
                          state.set_active_agent(agent)
                          agents.append(agent)
                          # reset wait message state
                          state.stored_data["seen_wait_message"] = False
                      assign_role_function = utils.get_assign_roles_fn(
                          self.world_module,
                          self.taskworld_map[world_type],
                      )
                      if assign_role_function is None:
                          assign_role_function = (
                              utils.default_assign_roles_fn
                          )
                      assign_role_function(agents)
                      # Allow task creator to filter out workers and run
                      # versions of the task that require fewer agents
                      agents = [
                          a for a in agents if a.disp_id is not None
                      ]
                      for a in agents:
                          # Remove selected workers from the agent pool
                          self.remove_agent_from_pool(
                              self.get_agent_state(a.id),
                              world_type=world_type,
                              mark_removed=False,
                          )
                      for a in agents:
                          partner_list = agents.copy()
                          partner_list.remove(a)
                          a.message_partners = partner_list
  
                      done_callback = (
                          self._get_done_callback_for_agents(
                              task_id, world_type, agents
                          )
                      )
  
                      # launch task world.
                      future = self.world_runner.launch_task_world(
                          task_id,
                          self.taskworld_map[world_type],
                          agents,
                      )
                      future.add_done_callback(done_callback)
                      self.active_worlds[task_id] = future
  
          time.sleep(utils.THREAD_MEDIUM_SLEEP)
  
imports: from abc import ABC, abstractmethod
  from asyncio import Future
  import copy
  import sys
  import logging
  import datetime
  import threading
  import time
  import traceback
  from typing import Dict, Any, Optional, List, Callable
  from parlai.chat_service.core.agents import ChatServiceAgent
  import parlai.chat_service.utils.logging
  import parlai.chat_service.utils.misc
  import parlai.chat_service.utils.server
  from parlai.chat_service.core.world_runner import ChatServiceWorldRunner
  from parlai.core.opt import Opt
label_desc: Use image node_id 30 for calling the ChatServiceManager obj's underlying member method code with example usage: obj.start_task() without return types