20
20
attach /2 ,
21
21
detach /2 ,
22
22
transfer /3 ,
23
- flow / 4 ,
24
- disposition / 5
23
+ disposition / 5 ,
24
+ flow_link / 4
25
25
]).
26
26
27
+ % % Manual session flow control is currently only used in tests.
28
+ -export ([flow /3 ]).
29
+
27
30
% % Private API
28
31
-export ([start_link /4 ,
29
32
socket_ready /2
51
54
[add /2 ,
52
55
diff /2 ]).
53
56
54
- -define (MAX_SESSION_WINDOW_SIZE , 65535 ).
57
+ % % By default, we want to keep the server's remote-incoming-window large at all times.
58
+ -define (DEFAULT_MAX_INCOMING_WINDOW , 100_000 ).
55
59
-define (UINT_OUTGOING_WINDOW , {uint , ? UINT_MAX }).
56
60
-define (INITIAL_OUTGOING_DELIVERY_ID , ? UINT_MAX ).
57
61
% % "The next-outgoing-id MAY be initialized to an arbitrary value" [2.5.6]
129
133
available = 0 :: non_neg_integer (),
130
134
drain = false :: boolean (),
131
135
partial_transfers :: undefined | {# 'v1_0.transfer' {}, [binary ()]},
132
- auto_flow :: never | {auto , RenewWhenBelow :: pos_integer (), Credit :: pos_integer ()},
136
+ auto_flow :: never | {RenewWhenBelow :: pos_integer (),
137
+ Credit :: pos_integer ()},
133
138
incoming_unsettled = #{} :: #{delivery_number () => ok },
134
139
footer_opt :: footer_opt () | undefined
135
140
}).
140
145
141
146
% % session flow control, see section 2.5.6
142
147
next_incoming_id :: transfer_number () | undefined ,
143
- incoming_window = ? MAX_SESSION_WINDOW_SIZE :: non_neg_integer (),
148
+ % % Can become negative if the peer overshoots our window.
149
+ incoming_window :: integer (),
150
+ auto_flow :: never | {RenewWhenBelow :: pos_integer (),
151
+ NewWindowSize :: pos_integer ()},
144
152
next_outgoing_id = ? INITIAL_OUTGOING_TRANSFER_ID :: transfer_number (),
145
153
remote_incoming_window = 0 :: non_neg_integer (),
146
154
remote_outgoing_window = 0 :: non_neg_integer (),
@@ -200,7 +208,17 @@ transfer(Session, Amqp10Msg, Timeout) ->
200
208
[Transfer | Sections ] = amqp10_msg :to_amqp_records (Amqp10Msg ),
201
209
gen_statem :call (Session , {transfer , Transfer , Sections }, Timeout ).
202
210
203
- flow (Session , Handle , Flow , RenewWhenBelow ) ->
211
+ -spec flow (pid (), non_neg_integer (), never | pos_integer ()) -> ok .
212
+ flow (Session , IncomingWindow , RenewWhenBelow ) when
213
+ % % Check that the RenewWhenBelow value make sense.
214
+ RenewWhenBelow =:= never orelse
215
+ is_integer (RenewWhenBelow ) andalso
216
+ RenewWhenBelow > 0 andalso
217
+ RenewWhenBelow =< IncomingWindow ->
218
+ gen_statem :cast (Session , {flow_session , IncomingWindow , RenewWhenBelow }).
219
+
220
+ -spec flow_link (pid (), link_handle (), # 'v1_0.flow' {}, never | pos_integer ()) -> ok .
221
+ flow_link (Session , Handle , Flow , RenewWhenBelow ) ->
204
222
gen_statem :cast (Session , {flow_link , Handle , Flow , RenewWhenBelow }).
205
223
206
224
% % Sending a disposition on a sender link (with receiver-settle-mode = second)
@@ -239,6 +257,9 @@ init([FromPid, Channel, Reader, ConnConfig]) ->
239
257
channel = Channel ,
240
258
reader = Reader ,
241
259
connection_config = ConnConfig ,
260
+ incoming_window = ? DEFAULT_MAX_INCOMING_WINDOW ,
261
+ auto_flow = {? DEFAULT_MAX_INCOMING_WINDOW div 2 ,
262
+ ? DEFAULT_MAX_INCOMING_WINDOW },
242
263
early_attach_requests = []},
243
264
{ok , unmapped , State }.
244
265
@@ -282,6 +303,7 @@ mapped(cast, 'end', State) ->
282
303
mapped (cast , {flow_link , OutHandle , Flow0 , RenewWhenBelow }, State0 ) ->
283
304
State = send_flow_link (OutHandle , Flow0 , RenewWhenBelow , State0 ),
284
305
{keep_state , State };
306
+ <<<<<<< HEAD
285
307
mapped (cast , {flow_session , Flow0 = # 'v1_0.flow' {incoming_window = {uint , IncomingWindow }}},
286
308
# state {next_incoming_id = NII ,
287
309
next_outgoing_id = NOI } = State ) ->
@@ -292,6 +314,18 @@ mapped(cast, {flow_session, Flow0 = #'v1_0.flow'{incoming_window = {uint, Incomi
292
314
ok = send (Flow , State ),
293
315
{keep_state , State # state {incoming_window = IncomingWindow }};
294
316
mapped (cast , # 'v1_0.end' {error = Err }, State ) ->
317
+ =======
318
+ mapped (cast , {flow_session , IncomingWindow , RenewWhenBelow }, State0 ) ->
319
+ AutoFlow = case RenewWhenBelow of
320
+ never -> never ;
321
+ _ -> {RenewWhenBelow , IncomingWindow }
322
+ end ,
323
+ State = State0 # state {incoming_window = IncomingWindow ,
324
+ auto_flow = AutoFlow },
325
+ send_flow_session (State ),
326
+ {keep_state , State };
327
+ mapped (cast , # 'v1_0.end' {} = End , State ) ->
328
+ >>>>>>> 35394625 a (Auto widen session incoming - window in AMQP 1.0 client )
295
329
% % We receive the first end frame, reply and terminate.
296
330
_ = send_end (State ),
297
331
% TODO: send notifications for links?
@@ -660,35 +694,44 @@ is_bare_message_section(_Section) ->
660
694
661
695
send_flow_link (OutHandle ,
662
696
# 'v1_0.flow' {link_credit = {uint , Credit }} = Flow0 , RenewWhenBelow ,
663
- # state {links = Links ,
664
- next_incoming_id = NII ,
665
- next_outgoing_id = NOI ,
666
- incoming_window = InWin } = State ) ->
697
+ # state {links = Links } = State ) ->
667
698
AutoFlow = case RenewWhenBelow of
668
699
never -> never ;
669
- Limit -> {auto , Limit , Credit }
700
+ _ -> {RenewWhenBelow , Credit }
670
701
end ,
671
702
#{OutHandle := # link {output_handle = H ,
672
703
role = receiver ,
673
704
delivery_count = DeliveryCount ,
674
705
available = Available } = Link } = Links ,
675
- Flow = Flow0 # 'v1_0.flow' {
676
- handle = uint (H ),
677
- % % "This value MUST be set if the peer has received the begin
678
- % % frame for the session, and MUST NOT be set if it has not." [2.7.4]
679
- next_incoming_id = maybe_uint (NII ),
680
- next_outgoing_id = uint (NOI ),
681
- outgoing_window = ? UINT_OUTGOING_WINDOW ,
682
- incoming_window = uint (InWin ),
683
- % % "In the event that the receiving link endpoint has not yet seen the
684
- % % initial attach frame from the sender this field MUST NOT be set." [2.7.4]
685
- delivery_count = maybe_uint (DeliveryCount ),
686
- available = uint (Available )},
706
+ Flow1 = Flow0 # 'v1_0.flow' {
707
+ handle = uint (H ),
708
+ % % "In the event that the receiving link endpoint has not yet seen the
709
+ % % initial attach frame from the sender this field MUST NOT be set." [2.7.4]
710
+ delivery_count = maybe_uint (DeliveryCount ),
711
+ available = uint (Available )},
712
+ Flow = set_flow_session_fields (Flow1 , State ),
687
713
ok = send (Flow , State ),
688
714
State # state {links = Links #{OutHandle =>
689
715
Link # link {link_credit = Credit ,
690
716
auto_flow = AutoFlow }}}.
691
717
718
+ send_flow_session (State ) ->
719
+ Flow = set_flow_session_fields (# 'v1_0.flow' {}, State ),
720
+ ok = send (Flow , State ).
721
+
722
+ set_flow_session_fields (Flow , # state {next_incoming_id = NID ,
723
+ incoming_window = IW ,
724
+ next_outgoing_id = NOI }) ->
725
+ Flow # 'v1_0.flow' {
726
+ % % "This value MUST be set if the peer has received the begin
727
+ % % frame for the session, and MUST NOT be set if it has not." [2.7.4]
728
+ next_incoming_id = maybe_uint (NID ),
729
+ % % IncomingWindow0 can be negative when the sending server overshoots our window.
730
+ % % We must set a floor of 0 in the FLOW frame because field incoming-window is an uint.
731
+ incoming_window = uint (max (0 , IW )),
732
+ next_outgoing_id = uint (NOI ),
733
+ outgoing_window = ? UINT_OUTGOING_WINDOW }.
734
+
692
735
build_frames (Channel , Trf , Bin , MaxPayloadSize , Acc )
693
736
when byte_size (Bin ) =< MaxPayloadSize ->
694
737
T = amqp10_framing :encode_bin (Trf # 'v1_0.transfer' {more = false }),
@@ -1020,17 +1063,21 @@ book_transfer_send(Num, #link{output_handle = Handle} = Link,
1020
1063
links = Links #{Handle => book_link_transfer_send (Link )}}.
1021
1064
1022
1065
book_partial_transfer_received (# state {next_incoming_id = NID ,
1023
- remote_outgoing_window = ROW } = State ) ->
1024
- State # state {next_incoming_id = add (NID , 1 ),
1025
- remote_outgoing_window = ROW - 1 }.
1066
+ incoming_window = IW ,
1067
+ remote_outgoing_window = ROW } = State0 ) ->
1068
+ State = State0 # state {next_incoming_id = add (NID , 1 ),
1069
+ incoming_window = IW - 1 ,
1070
+ remote_outgoing_window = ROW - 1 },
1071
+ maybe_widen_incoming_window (State ).
1026
1072
1027
1073
book_transfer_received (State = # state {connection_config =
1028
1074
#{transfer_limit_margin := Margin }},
1029
1075
# link {link_credit = Margin } = Link ) ->
1030
1076
{transfer_limit_exceeded , Link , State };
1031
1077
book_transfer_received (# state {next_incoming_id = NID ,
1078
+ incoming_window = IW ,
1032
1079
remote_outgoing_window = ROW ,
1033
- links = Links } = State ,
1080
+ links = Links } = State0 ,
1034
1081
# link {output_handle = OutHandle ,
1035
1082
delivery_count = DC ,
1036
1083
link_credit = LC ,
@@ -1040,19 +1087,31 @@ book_transfer_received(#state{next_incoming_id = NID,
1040
1087
% % "the receiver MUST maintain a floor of zero in its
1041
1088
% % calculation of the value of available" [2.6.7]
1042
1089
available = max (0 , Avail - 1 )},
1043
- State1 = State # state {links = Links #{OutHandle => Link1 },
1044
- next_incoming_id = add (NID , 1 ),
1045
- remote_outgoing_window = ROW - 1 },
1090
+ State1 = State0 # state {links = Links #{OutHandle => Link1 },
1091
+ next_incoming_id = add (NID , 1 ),
1092
+ incoming_window = IW - 1 ,
1093
+ remote_outgoing_window = ROW - 1 },
1094
+ State = maybe_widen_incoming_window (State1 ),
1046
1095
case Link1 of
1047
1096
# link {link_credit = 0 ,
1048
1097
auto_flow = never } ->
1049
- {credit_exhausted , Link1 , State1 };
1098
+ {credit_exhausted , Link1 , State };
1050
1099
_ ->
1051
- {ok , Link1 , State1 }
1100
+ {ok , Link1 , State }
1052
1101
end .
1053
1102
1103
+ maybe_widen_incoming_window (
1104
+ State0 = # state {incoming_window = IncomingWindow ,
1105
+ auto_flow = {RenewWhenBelow , NewWindowSize }})
1106
+ when IncomingWindow < RenewWhenBelow ->
1107
+ State = State0 # state {incoming_window = NewWindowSize },
1108
+ send_flow_session (State ),
1109
+ State ;
1110
+ maybe_widen_incoming_window (State ) ->
1111
+ State .
1112
+
1054
1113
auto_flow (# link {link_credit = LC ,
1055
- auto_flow = {auto , RenewWhenBelow , Credit },
1114
+ auto_flow = {RenewWhenBelow , Credit },
1056
1115
output_handle = OutHandle ,
1057
1116
incoming_unsettled = Unsettled },
1058
1117
State )
@@ -1218,6 +1277,7 @@ format_status(Status = #{data := Data0}) ->
1218
1277
remote_channel = RemoteChannel ,
1219
1278
next_incoming_id = NextIncomingId ,
1220
1279
incoming_window = IncomingWindow ,
1280
+ auto_flow = SessionAutoFlow ,
1221
1281
next_outgoing_id = NextOutgoingId ,
1222
1282
remote_incoming_window = RemoteIncomingWindow ,
1223
1283
remote_outgoing_window = RemoteOutgoingWindow ,
@@ -1282,6 +1342,7 @@ format_status(Status = #{data := Data0}) ->
1282
1342
remote_channel => RemoteChannel ,
1283
1343
next_incoming_id => NextIncomingId ,
1284
1344
incoming_window => IncomingWindow ,
1345
+ auto_flow => SessionAutoFlow ,
1285
1346
next_outgoing_id => NextOutgoingId ,
1286
1347
remote_incoming_window => RemoteIncomingWindow ,
1287
1348
remote_outgoing_window => RemoteOutgoingWindow ,
0 commit comments