Non-Destructive Queue Receive State Machine

The state machine for a non-destructive receive has some noticeable similarities to the state machine for a general-purpose communication object, but it's intentional for these two state machines to be different. A non-destructive receive tries with minimal overhead to provide support for at-least once delivery of messages, and through transactions support for exactly-once delivery of messages.

A fresh receive starts in the Received state, corresponding to a message that is a locked and unprocessed. As I mentioned before, there's no publicly visible state corresponding to an unlocked and unprocessed message.

Completing the receive can only be done while in the Received state. It's illegal to try to complete a receive more than once. Completion converts the non-destructive receive to a destructive receive, with the assistance of a transaction if one is provided. Calling Complete moves the state from Received to Completing. While in the Completing state, the OnComplete callback is invoked. Finally, the state moves from Completing to Completed when all of the preparations are complete.

Abandoning the receive can be done while in the Received, Abandoning, and Abandoned states. It's legal to try to abandon the receive more than once, although the successive abandons don't have any additional effect. Abandoning converts the non-destructive receive back into an unlocked message. Calling Abandon moves the state from Received to Abandoning. While in the Abandoning state, the OnAbandon callback is invoked. Finally, the state moves from Abandoning to Abandoned when all of the preparations are complete.

Faulting the receive can be done while in any state. It's legal to try to fault the receive after it has been completed, abandoned, or faulted before, but the successive faults don't have any additional effect. Faulting places the receive in doubt. It's no longer possible to determine from within the system what the state of the resource is. Fault races with Abandon such that the abandon might win and you get a fast unlock behavior or the fault might win and you get a slow unlock behavior (due to the lock timing out). In either case, the result is that the message is unlocked. Fault races with Complete such that the complete might win and you delete the message or the fault might win and you unlock the message. A message that fails to complete because of a fault might be presented more times than necessary but not fewer times than necessary. By employing a transaction it's possible to maintain a strong delivery guarantee because the doubt of the receive does not spread to the transaction.