|
|
|
@ -29,6 +29,9 @@ |
|
|
|
|
|
|
|
|
|
"""State and behavior for ticket transmission during an operation.""" |
|
|
|
|
|
|
|
|
|
import collections |
|
|
|
|
import enum |
|
|
|
|
|
|
|
|
|
from grpc.framework.core import _constants |
|
|
|
|
from grpc.framework.core import _interfaces |
|
|
|
|
from grpc.framework.foundation import callable_util |
|
|
|
@ -47,6 +50,31 @@ def _explode_completion(completion): |
|
|
|
|
links.Ticket.Termination.COMPLETION) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class _Abort( |
|
|
|
|
collections.namedtuple( |
|
|
|
|
'_Abort', ('kind', 'termination', 'code', 'details',))): |
|
|
|
|
"""Tracks whether the operation aborted and what is to be done about it. |
|
|
|
|
|
|
|
|
|
Attributes: |
|
|
|
|
kind: A Kind value describing the overall kind of the _Abort. |
|
|
|
|
termination: A links.Ticket.Termination value to be sent to the other side |
|
|
|
|
of the operation. Only valid if kind is Kind.ABORTED_NOTIFY_NEEDED. |
|
|
|
|
code: A code value to be sent to the other side of the operation. Only |
|
|
|
|
valid if kind is Kind.ABORTED_NOTIFY_NEEDED. |
|
|
|
|
details: A details value to be sent to the other side of the operation. |
|
|
|
|
Only valid if kind is Kind.ABORTED_NOTIFY_NEEDED. |
|
|
|
|
""" |
|
|
|
|
|
|
|
|
|
@enum.unique |
|
|
|
|
class Kind(enum.Enum): |
|
|
|
|
NOT_ABORTED = 'not aborted' |
|
|
|
|
ABORTED_NOTIFY_NEEDED = 'aborted notify needed' |
|
|
|
|
ABORTED_NO_NOTIFY = 'aborted no notify' |
|
|
|
|
|
|
|
|
|
_NOT_ABORTED = _Abort(_Abort.Kind.NOT_ABORTED, None, None, None) |
|
|
|
|
_ABORTED_NO_NOTIFY = _Abort(_Abort.Kind.ABORTED_NO_NOTIFY, None, None, None) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TransmissionManager(_interfaces.TransmissionManager): |
|
|
|
|
"""An _interfaces.TransmissionManager that sends links.Tickets.""" |
|
|
|
|
|
|
|
|
@ -79,8 +107,7 @@ class TransmissionManager(_interfaces.TransmissionManager): |
|
|
|
|
self._initial_metadata = None |
|
|
|
|
self._payloads = [] |
|
|
|
|
self._completion = None |
|
|
|
|
self._aborted = False |
|
|
|
|
self._abortion_outcome = None |
|
|
|
|
self._abort = _NOT_ABORTED |
|
|
|
|
self._transmitting = False |
|
|
|
|
|
|
|
|
|
def set_expiration_manager(self, expiration_manager): |
|
|
|
@ -94,24 +121,15 @@ class TransmissionManager(_interfaces.TransmissionManager): |
|
|
|
|
A links.Ticket to be sent to the other side of the operation or None if |
|
|
|
|
there is nothing to be sent at this time. |
|
|
|
|
""" |
|
|
|
|
if self._aborted: |
|
|
|
|
if self._abortion_outcome is None: |
|
|
|
|
return None |
|
|
|
|
else: |
|
|
|
|
termination = _constants.ABORTION_OUTCOME_TO_TICKET_TERMINATION[ |
|
|
|
|
self._abortion_outcome] |
|
|
|
|
if termination is None: |
|
|
|
|
return None |
|
|
|
|
else: |
|
|
|
|
self._abortion_outcome = None |
|
|
|
|
if self._completion is None: |
|
|
|
|
code, message = None, None |
|
|
|
|
else: |
|
|
|
|
code, message = self._completion.code, self._completion.message |
|
|
|
|
return links.Ticket( |
|
|
|
|
self._operation_id, self._lowest_unused_sequence_number, None, |
|
|
|
|
None, None, None, None, None, None, None, code, message, |
|
|
|
|
termination, None) |
|
|
|
|
if self._abort.kind is _Abort.Kind.ABORTED_NO_NOTIFY: |
|
|
|
|
return None |
|
|
|
|
elif self._abort.kind is _Abort.Kind.ABORTED_NOTIFY_NEEDED: |
|
|
|
|
termination = self._abort.termination |
|
|
|
|
code, details = self._abort.code, self._abort.details |
|
|
|
|
self._abort = _ABORTED_NO_NOTIFY |
|
|
|
|
return links.Ticket( |
|
|
|
|
self._operation_id, self._lowest_unused_sequence_number, None, None, |
|
|
|
|
None, None, None, None, None, None, code, details, termination, None) |
|
|
|
|
|
|
|
|
|
action = False |
|
|
|
|
# TODO(nathaniel): Support other subscriptions. |
|
|
|
@ -174,6 +192,7 @@ class TransmissionManager(_interfaces.TransmissionManager): |
|
|
|
|
return |
|
|
|
|
else: |
|
|
|
|
with self._lock: |
|
|
|
|
self._abort = _ABORTED_NO_NOTIFY |
|
|
|
|
if self._termination_manager.outcome is None: |
|
|
|
|
self._termination_manager.abort(base.Outcome.TRANSMISSION_FAILURE) |
|
|
|
|
self._expiration_manager.terminate() |
|
|
|
@ -201,6 +220,9 @@ class TransmissionManager(_interfaces.TransmissionManager): |
|
|
|
|
|
|
|
|
|
def advance(self, initial_metadata, payload, completion, allowance): |
|
|
|
|
"""See _interfaces.TransmissionManager.advance for specification.""" |
|
|
|
|
if self._abort.kind is not _Abort.Kind.NOT_ABORTED: |
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
effective_initial_metadata = initial_metadata |
|
|
|
|
effective_payload = payload |
|
|
|
|
effective_completion = completion |
|
|
|
@ -246,7 +268,9 @@ class TransmissionManager(_interfaces.TransmissionManager): |
|
|
|
|
|
|
|
|
|
def timeout(self, timeout): |
|
|
|
|
"""See _interfaces.TransmissionManager.timeout for specification.""" |
|
|
|
|
if self._transmitting: |
|
|
|
|
if self._abort.kind is not _Abort.Kind.NOT_ABORTED: |
|
|
|
|
return |
|
|
|
|
elif self._transmitting: |
|
|
|
|
self._timeout = timeout |
|
|
|
|
else: |
|
|
|
|
ticket = links.Ticket( |
|
|
|
@ -257,7 +281,9 @@ class TransmissionManager(_interfaces.TransmissionManager): |
|
|
|
|
|
|
|
|
|
def allowance(self, allowance): |
|
|
|
|
"""See _interfaces.TransmissionManager.allowance for specification.""" |
|
|
|
|
if self._transmitting or not self._payloads: |
|
|
|
|
if self._abort.kind is not _Abort.Kind.NOT_ABORTED: |
|
|
|
|
return |
|
|
|
|
elif self._transmitting or not self._payloads: |
|
|
|
|
self._remote_allowance += allowance |
|
|
|
|
else: |
|
|
|
|
self._remote_allowance += allowance - 1 |
|
|
|
@ -283,20 +309,17 @@ class TransmissionManager(_interfaces.TransmissionManager): |
|
|
|
|
|
|
|
|
|
def abort(self, outcome, code, message): |
|
|
|
|
"""See _interfaces.TransmissionManager.abort for specification.""" |
|
|
|
|
if self._transmitting: |
|
|
|
|
self._aborted, self._abortion_outcome = True, outcome |
|
|
|
|
else: |
|
|
|
|
self._aborted = True |
|
|
|
|
if outcome is not None: |
|
|
|
|
termination = _constants.ABORTION_OUTCOME_TO_TICKET_TERMINATION[ |
|
|
|
|
outcome] |
|
|
|
|
if termination is not None: |
|
|
|
|
if self._completion is None: |
|
|
|
|
code, message = None, None |
|
|
|
|
else: |
|
|
|
|
code, message = self._completion.code, self._completion.message |
|
|
|
|
ticket = links.Ticket( |
|
|
|
|
self._operation_id, self._lowest_unused_sequence_number, None, |
|
|
|
|
None, None, None, None, None, None, None, code, message, |
|
|
|
|
termination, None) |
|
|
|
|
self._transmit(ticket) |
|
|
|
|
if self._abort.kind is _Abort.Kind.NOT_ABORTED: |
|
|
|
|
termination = _constants.ABORTION_OUTCOME_TO_TICKET_TERMINATION.get( |
|
|
|
|
outcome) |
|
|
|
|
if termination is None: |
|
|
|
|
self._abort = _ABORTED_NO_NOTIFY |
|
|
|
|
elif self._transmitting: |
|
|
|
|
self._abort = _Abort( |
|
|
|
|
_Abort.Kind.ABORTED_NOTIFY_NEEDED, termination, code, message) |
|
|
|
|
else: |
|
|
|
|
ticket = links.Ticket( |
|
|
|
|
self._operation_id, self._lowest_unused_sequence_number, None, |
|
|
|
|
None, None, None, None, None, None, None, code, message, |
|
|
|
|
termination, None) |
|
|
|
|
self._transmit(ticket) |
|
|
|
|