Send messagedata while the node is healthy until it's acknowledged.

    Note:
        backoff must be an infinite iterator, otherwise this task will
        become a hot loop.

def retry_with_recovery(
        transport: 'UDPTransport',
        messagedata: bytes,
        message_id: UDPMessageID,
        recipient: Address,
        stop_event: Event,
        event_healthy: Event,
        event_unhealthy: Event,
        backoff: Iterable[int],
) -> bool:
    """ Send messagedata while the node is healthy until it's acknowledged.

    Note:
        backoff must be an infinite iterator, otherwise this task will
        become a hot loop.
    """

    # The underlying unhealthy will be cleared, care must be taken to properly
    # clear stop_or_unhealthy too.
    stop_or_unhealthy = event_first_of(
        stop_event,
        event_unhealthy,
    )

    acknowledged = False
    while not stop_event.is_set() and not acknowledged:

        # Packets must not be sent to an unhealthy node, nor should the task
        # wait for it to become available if the message has been acknowledged.
        if event_unhealthy.is_set():
            log.debug(
                'waiting for recipient to become available',
                node=pex(transport.raiden.address),
                recipient=pex(recipient),
            )
            wait_recovery(
                stop_event,
                event_healthy,
            )

            # Assume wait_recovery returned because unhealthy was cleared and
            # continue execution, this is safe to do because stop_event is
            # checked below.
            stop_or_unhealthy.clear()

            if stop_event.is_set():
                return acknowledged

        acknowledged = retry(
            transport,
            messagedata,
            message_id,
            recipient,

            # retry will stop when this event is set, allowing this task to
            # wait for recovery when the node becomes unhealthy or to quit if
            # the stop event is set.
            stop_or_unhealthy,

            # Intentionally reusing backoff to restart from the last
            # timeout/number of iterations.
            backoff,
        )

    return acknowledged