def METHOD_NAME(self, keep_running):
    """
    Runs a loop ticking the target control. The callback is called each time and
    can be used to determine if the loop should keep running. The default callback
    continues running until at least one capture has been made.
    Either way, if the target application closes and the target control connection
    is lost, the loop exits and the function returns.
    :param keep_running: A callback function to call each tick. Returns ``True`` if
      the loop should continue, or ``False`` otherwise.
    """
    if self.control is None:
        return
    start_time = datetime.datetime.now(datetime.timezone.utc)
    while keep_running(self):
        msg: rd.TargetControlMessage = self.control.ReceiveMessage(None)
        if (datetime.datetime.now(datetime.timezone.utc) - start_time).total_seconds() > self._timeout:
            log.error("Timed out")
            break
        # If we got a graceful or non-graceful shutdown, break out of the loop
        if (msg.type == rd.TargetControlMessageType.Disconnected or
                not self.control.Connected()):
            break
        # If we got a new capture, add it to our list
        if msg.type == rd.TargetControlMessageType.NewCapture:
            self._captures.append(msg.newCapture)
            continue
        # Similarly for a new child
        if msg.type == rd.TargetControlMessageType.NewChild:
            self._children.append(msg.newChild)
            continue
    # Shut down the connection
    self.control.Shutdown()
    self.control = None
    # If we should make sure the application is killed when we exit, do that now
    if self._exit_kill:
        # Try 5 times to kill the application. This may fail if the application exited already
        for attempt in range(5):
            try:
                os.kill(self._pid, signal.SIGTERM)
                time.sleep(1)
                return
            except Exception:
                # Ignore errors killing the program
                continue