code: def start(self, timeout=None):
          
  
          # we lazily create our process delegate (with same arguments)
          if self.daemon:
              daemonic = True
          else:
              daemonic = False
  
          pargs = self._pargs.copy()
          pargs.pop('daemonic', None)
  
          self._process = multiprocessing.Process(**pargs)
  
          self._process.daemon = daemonic
  
          if self.is_alive():
              # if already started, we shutdown and join before restarting
              # not timeout will bock here (default join behavior).
              # otherwise we simply use the same timeout.
              self.shutdown(join=True, timeout=timeout)  # TODO : only restart if no error (check exitcode)
              self.start(timeout=timeout)  # recursive to try again if needed
          else:
              self._process.start()
  
          # timeout None means we want to wait and ensure it has started
          # deterministic behavior, like is_alive() from multiprocess.Process is always true after start()
          if self.started.wait(timeout=timeout):  # blocks until we know true or false
  
  
              return True
              # return self._svc_address  # returning the zmp url as a way to connect to the node
              # CAREFUL : doesnt make sense if this node only run a one-time task...
          # TODO: futures and ThreadPoolExecutor (so we dont need to manage the pool ourselves)
          else:
              return False
docs: Start child process
          :param timeout: the maximum time to wait for child process to report it has actually started.
          None waits until the context manager has been entered, but update might not have been called yet.