1
=============================================================================
2
Channel implementation notes
3
=============================================================================
6
The public API of channels make them appear either opened or closed.
7
When a channel is closed, we can't send any more items, and it will not
8
receive any more items than already queued.
10
Callbacks make the situation slightly more subtle. Callbacks are
11
attached to the ChannelFactory object, so that Channel objects can be
12
garbage-collected and still leave behind an active callback that can
13
continue to receive items.
15
The CHANNEL_CLOSE message is sent when a channel id is about to be removed
16
from the ChannelFactory, which means when the Channel object has been
17
garbage-collected *and* there is no callback any more.
19
If a Channel object is garbage-collected but the ChannelFactory has a
20
callback for it, a CHANNEL_LAST_MESSAGE message is sent. It is only useful
21
if both sides' Channel objects have an associated callback. In this
22
situation, CHANNEL_LAST_MESSAGE allows its receiver to un-register its own
23
callback; if/when in addition the receiver side also looses the last
24
reference to its Channel object, the Channel is closed. So in this particular
25
situation both sides must forget about the Channel object for it to be
30
gateway <---> channelfactory ---> {id: weakref(channel)}
35
State and invariants of Channel objects
36
---------------------------------------
38
_channels and _callbacks are dictionaries on the ChannelFactory.
39
Other attributes are on the Channel objects.
41
All states are valid at any time (even with multithreading) unless
42
marked with {E}, which means that they may be temporary invalid.
43
They are eventually restored.
46
States ("sendonly" means opened but won't receive any more items):
48
opened sendonly closed deleted
49
================= ============== ================== ===============
50
not _closed not _closed _closed <no ref left>
51
not _receiveclosed _receiveclosed {E} _receiveclosed
53
In the presence of callbacks, "deleted" does not imply "closed" nor "sendonly".
54
It only means that no more items can be sent. The (logical) channel can
55
continue to receive data via the call-back even if the channel object no
59
The two kinds of channels, with or without callback:
61
items read by receive() has a callback
62
============================= =======================================
63
_items is a Queue _items is None
65
state==opened: id in _callbacks
66
{E} state==sendonly: there is {E} state!=opened: id not in _callbacks
67
an ENDMARKER in _items
68
{E} state==closed: there is
69
an ENDMARKER in _items
71
Callback calls should be considered asynchronuous. The channel can be in any
72
state and change its state while the callback runs.
75
The ChannelFactory's WeakValueDictionary _channels maps some ids to their
76
channel object, depending on their state:
78
opened sendonly closed deleted
79
================= ============== ================ ===============
80
id in _channels {E} not in {E} not in not in
83
All received RemoteErrors are handled exactly once: they are normally
84
re-raised once in waitclose() or receive(). If it is not possible, they are
85
at the moment dumped to stderr. (XXX should use logging/tracing)
86
Only channels in {E} "closed" state can hold RemoteErrors.
91
* close() returns with the channel in "closed" state
92
* send() either send the data or raise if "closed"
93
* receive() wait for the next item. If no item left and the state
94
changes to non-"opened", raise
95
* waitclose() wait for a non-"opened" state
98
Assuming the channel is connected and the connection is alive, the local state
99
eventually influences the state of the corresponding remote channel object:
101
local | opened sendonly closed deleted
103
=======================================================
105
opened | ok n/a (1) (2)
107
sendonly | n/a n/a n/a ok
109
closed | (1) n/a ok ok
111
deleted | (2) ok ok ok
113
(1) The side with the closed channel object must send a CHANNEL_CLOSE message,
114
which will eventually put the other side's channel in "closed" state if
115
it is still "opened".
117
(2) If the deleted channel has no callback, this is equivalent to (1).
118
Otherwide, the side with the deleted channel must send a
119
CHANNEL_LAST_MESSAGE, which will eventually put the other side's channel in
120
"sendonly" state if it is still "opened".
122
n/a These configuration should never occur.