docs: def dispatch(restricted=False):
      """
      Dispatch registered handlers.
  
      When dispatching in restricted mode, only matching hook handlers are executed.
  
      Handlers are dispatched according to the following rules:
  
      * Handlers are repeatedly tested and invoked in iterations, until the system
        settles into quiescence (that is, until no new handlers match to be invoked).
  
      * In the first iteration, :func:`@hook <charms.reactive.decorators.hook>`
        and :func:`@action <charms.reactive.decorators.action>` handlers will
        be invoked, if they match.
  
      * In subsequent iterations, other handlers are invoked, if they match.
  
      * Added flags will not trigger new handlers until the next iteration,
        to ensure that chained flags are invoked in a predictable order.
  
      * Removed flags will cause the current set of matched handlers to be
        re-tested, to ensure that no handler is invoked after its matching
        flag has been removed.
  
      * Other than the guarantees mentioned above, the order in which matching
        handlers are invoked is undefined.
  
      * Flags are preserved between hook and action invocations, and all matching
        handlers are re-invoked for every hook and action.  There are
        :doc:`decorators <charms.reactive.decorators>` and
        :doc:`helpers <charms.reactive.helpers>`
        to prevent unnecessary reinvocations, such as
        :func:`~charms.reactive.decorators.only_once`.
      """
      FlagWatch.reset()
  
      def _test(to_test):
          return list(filter(lambda h: h.test(), to_test))
  
      def _invoke(to_invoke):
          while to_invoke:
              unitdata.kv().set('reactive.dispatch.removed_state', False)
              for handler in list(to_invoke):
                  to_invoke.remove(handler)
                  hookenv.log('Invoking reactive handler: %s' % handler.id(), level=hookenv.INFO)
                  handler.invoke()
                  if unitdata.kv().get('reactive.dispatch.removed_state'):
                      # re-test remaining handlers
                      to_invoke = _test(to_invoke)
                      break
          FlagWatch.commit()
  
      tracer().start_dispatch()
  
      # When in restricted context, only run hooks for that context.
      if restricted:
          unitdata.kv().set('reactive.dispatch.phase', 'restricted')
          hook_handlers = _test(Handler.get_handlers())
          tracer().start_dispatch_phase('restricted', hook_handlers)
          _invoke(hook_handlers)
          return
  
      unitdata.kv().set('reactive.dispatch.phase', 'hooks')
      hook_handlers = _test(Handler.get_handlers())
      tracer().start_dispatch_phase('hooks', hook_handlers)
      _invoke(hook_handlers)
  
      unitdata.kv().set('reactive.dispatch.phase', 'other')
      for i in range(100):
          FlagWatch.iteration(i)
          other_handlers = _test(Handler.get_handlers())
          if i == 0:
              tracer().start_dispatch_phase('other', other_handlers)
          tracer().start_dispatch_iteration(i, other_handlers)
          if not other_handlers:
              break
          _invoke(other_handlers)
  
      FlagWatch.reset()