Writing
data is considerably simpler, so we will start with looking at write.
Channel.Write() simply calls Connection.Write(), so the bulk of the logic is in
Connection. When Connection receive a Write request, it breakdown the request
into frames. Each frame contain a simple header that indicate which Channel it
comes from, and the size of the frame so that it can be decoded later, and the
bulk of the frame is then forwarded to SenderActor for processing. Note that
while frame headers are newly allocated memory blocks, the frames are simply
created using integer indexes so that the buffer is not copied.
After
receiving frames, SenderActor keeps them in a list of SegmentHandlePairs. A
SegmentHandlePair is a pair of Segment (of data to send) and a Completion
handle to notify the sender the send is completed. Only the last frame has the
segment handle pair attached. Normally it will just send the data through the
socket, but it must be careful not to do it when the last sending is in
progress.
On
writing to transport completed, TCP will give us a notification how many bytes
are written. SenderActor will receive this notification and then go back to the
segment handle pairs. A scanning of the list will be done to see if there are
completion handle to update. It is quite probable that not all data are sent,
in that case we need to update the list with the right index, and then request
the send to transport again.
These
logic are best illustrated by an example. For simplicity, the frame size is 8
and the channel id/size take 1 byte.
Write request with
10 bytes comes from channel 1
1 2 3 4 5 6 7 8 9 10
The data are packed
into frames
[Channel 1, Size 8]
1 2 3 4 5 6 7 8 [Channel 1 Size 2] 9 10
The frames are sent
to SenderActor, sender Actor marks the last segment with the completion
handle
[Channel 1, Size 4]
1 2 3 4 [Channel 1 Size 4] 5 6 7 8 [Channel 1 Size 2] 9 10
Sender Actor send
these to the transports
Write request with 7
bytes comes from Channel 2
The data are packed
into frames
[Channel 2, Size 7] A
B C D E F G
[Channel 1, Size 4]
1 2 3 4 [Channel 1 Size 4] 5 6 7 8 [Channel 1 Size 2] 9 10 [Channel 2, Size 7] A B C D E F G
Sender Actor can’t
send the data yet because it is transport write in progress.
Transport reports 11
bytes are written
Sender Actor updates
its data structure
Sender Actor realize
there are more data to send, so it request transport to send these again.
Transport reports 6
bytes are written
Sender Actor updates
its data structure
Sender Actor knows
it needs to notify a completion handle a send is completed.
Sender Actor realize
there are more data to send, so it request transport to send these again.
|
Hopefully
one can appreciate how actor makes the concurrency issue trivial here. If we
had to deal with concurrency here it would be more complicated. With
SenderActor, we can happily ignore the fact that the Channels and transport are
concurrent because we are processing the messages one by one, the data
structure need no locks.
No comments :
Post a Comment