def single_queue_send( transport: 'UDPTransport', recipient: typing.Address, queue: Queue_T, event_stop: Event, event_healthy: Event, event_unhealthy: Event, message_retries: int, message_retry_timeout: int, message_retry_max_timeout: int, ): """ Handles a single message queue for `recipient`. Notes: - This task must be the only consumer of queue. - This task can be killed at any time, but the intended usage is to stop it with the event_stop. - If there are many queues for the same recipient, it is the caller's responsibility to not start them together to avoid congestion. - This task assumes the endpoint is never cleared after it's first known. If this assumption changes the code must be updated to handle unknown addresses. """ # A NotifyingQueue is required to implement cancelability, otherwise the # task cannot be stopped while the greenlet waits for an element to be # inserted in the queue. if not isinstance(queue, NotifyingQueue): raise ValueError('queue must be a NotifyingQueue.') # Reusing the event, clear must be carefully done data_or_stop = event_first_of( queue, event_stop, ) # Wait for the endpoint registration or to quit event_first_of( event_healthy, event_stop, ).wait() while True: data_or_stop.wait() if event_stop.is_set(): return # The queue is not empty at this point, so this won't raise Empty. # This task being the only consumer is a requirement. (messagedata, message_id) = queue.peek(block=False) backoff = timeout_exponential_backoff( message_retries, message_retry_timeout, message_retry_max_timeout, ) try: acknowledged = retry_with_recovery( transport, messagedata, message_id, recipient, event_stop, event_healthy, event_unhealthy, backoff, ) except RaidenShuttingDown: # For a clean shutdown process return if acknowledged: queue.get() # Checking the length of the queue does not trigger a # context-switch, so it's safe to assume the length of the queue # won't change under our feet and when a new item will be added the # event will be set again. if not queue: data_or_stop.clear() if event_stop.is_set(): return
def single_queue_send( transport: 'UDPTransport', recipient: typing.Address, queue: Queue_T, queue_identifier: QueueIdentifier, event_stop: Event, event_healthy: Event, event_unhealthy: Event, message_retries: int, message_retry_timeout: int, message_retry_max_timeout: int, ): """ Handles a single message queue for `recipient`. Notes: - This task must be the only consumer of queue. - This task can be killed at any time, but the intended usage is to stop it with the event_stop. - If there are many queues for the same recipient, it is the caller's responsibility to not start them together to avoid congestion. - This task assumes the endpoint is never cleared after it's first known. If this assumption changes the code must be updated to handle unknown addresses. """ # A NotifyingQueue is required to implement cancelability, otherwise the # task cannot be stopped while the greenlet waits for an element to be # inserted in the queue. if not isinstance(queue, NotifyingQueue): raise ValueError('queue must be a NotifyingQueue.') # Reusing the event, clear must be carefully done data_or_stop = event_first_of( queue, event_stop, ) # Wait for the endpoint registration or to quit log.debug( 'queue: waiting for node to become healthy', node=pex(transport.raiden.address), queue_identifier=queue_identifier, queue_size=len(queue), ) event_first_of( event_healthy, event_stop, ).wait() log.debug( 'queue: processing queue', node=pex(transport.raiden.address), queue_identifier=queue_identifier, queue_size=len(queue), ) while True: data_or_stop.wait() if event_stop.is_set(): log.debug( 'queue: stopping', node=pex(transport.raiden.address), queue_identifier=queue_identifier, queue_size=len(queue), ) return # The queue is not empty at this point, so this won't raise Empty. # This task being the only consumer is a requirement. (messagedata, message_id) = queue.peek(block=False) log.debug( 'queue: sending message', node=pex(transport.raiden.address), recipient=pex(recipient), msgid=message_id, queue_identifier=queue_identifier, queue_size=len(queue), ) backoff = timeout_exponential_backoff( message_retries, message_retry_timeout, message_retry_max_timeout, ) acknowledged = retry_with_recovery( transport, messagedata, message_id, recipient, event_stop, event_healthy, event_unhealthy, backoff, ) if acknowledged: queue.get() # Checking the length of the queue does not trigger a # context-switch, so it's safe to assume the length of the queue # won't change under our feet and when a new item will be added the # event will be set again. if not queue: data_or_stop.clear() if event_stop.is_set(): return