docs: def run(self,
              timeout=None,
              return_when=None,
              return_exceptions=None,
              ignore_empty=None):
          """
          Executes the registered coroutines in the executor queue.
  
          Arguments:
              timeout (int/float): max execution timeout. No limit by default.
              return_exceptions (bool): in case of coroutine exception.
              return_when (str): sets when coroutine should be resolved.
                  See `asyncio.wait`_ for supported values.
              ignore_empty (bool, optional): do not raise an exception if there are
                  no coroutines to schedule are empty.
  
          Returns:
              asyncio.Future (tuple): two sets of Futures: ``(done, pending)``
  
          Raises:
              ValueError: if there is no coroutines to schedule.
              RuntimeError: if executor is still running.
              TimeoutError: if execution takes more than expected.
  
          .. _asyncio.wait: https://docs.python.org/3/library/asyncio-task.html#asyncio.wait  # noqa
          """
          # Only allow 1 concurrent execution
          if self.running:
              raise RuntimeError('paco: executor is already running')
  
          # Overwrite ignore empty behaviour, if explicitly defined
          ignore_empty = (self.ignore_empty if ignore_empty is None
                          else ignore_empty)
  
          # Check we have coroutines to schedule
          if len(self.pool) == 0:
              # If ignore empty mode enabled, just return an empty tuple
              if ignore_empty:
                  return (tuple(), tuple())
              # Othwerise raise an exception
              raise ValueError('paco: pool of coroutines is empty')
  
          # Set executor state to running
          self.running = True
  
          # Configure return exceptions
          if return_exceptions is not None:
              self.return_exceptions = return_exceptions
  
          if return_exceptions is False and return_when is None:
              return_when = 'FIRST_EXCEPTION'
  
          if return_when is None:
              return_when = 'ALL_COMPLETED'
  
          # Trigger pre-execution event
          yield from self.observer.trigger('start', self)
  
          # Sequential coroutines execution
          if self.limit == 1:
              done, pending = yield from self._run_sequentially()
  
          # Concurrent execution based on configured limit
          if self.limit != 1:
              done, pending = yield from self._run_concurrently(
                  timeout=timeout,
                  return_when=return_when)
  
          # Reset internal state and queue
          self.running = False
  
          # Raise exception, if needed
          if self.return_exceptions is False and self.errors:
              err = self.errors[0]
              err.errors = self.errors[1:]
              raise err
  
          # Trigger pre-execution event
          yield from self.observer.trigger('finish', self)
  
          # Reset executor state to defaults after each execution
          self.reset()
  
          # Return resultant futures in two tuples
          return done, pending