@@ -19,6 +19,7 @@ import (
19
19
"errors"
20
20
"fmt"
21
21
"io"
22
+ "sync/atomic"
22
23
23
24
"github.com/arduino/arduino-cli/arduino/monitors"
24
25
rpc "github.com/arduino/arduino-cli/rpc/monitor"
@@ -79,6 +80,14 @@ func (s *MonitorService) StreamingOpen(stream rpc.Monitor_StreamingOpenServer) e
79
80
streamClosed := make (chan error )
80
81
targetClosed := make (chan error )
81
82
83
+ // set rate limiting window
84
+ bufferSize := int (config .GetRecvRateLimitBuffer ())
85
+ buffer := make ([]byte , bufferSize )
86
+ bufferUsed := 0
87
+
88
+ rateLimitEnabled := (bufferSize > 0 )
89
+ var writeSlots int32
90
+
82
91
// now we can read the other messages and re-route to the monitor...
83
92
go func () {
84
93
for {
@@ -95,6 +104,11 @@ func (s *MonitorService) StreamingOpen(stream rpc.Monitor_StreamingOpenServer) e
95
104
break
96
105
}
97
106
107
+ if rateLimitEnabled {
108
+ // Increase rate limiter write slots
109
+ atomic .AddInt32 (& writeSlots , msg .GetRecvAcknowledge ())
110
+ }
111
+
98
112
if _ , err := mon .Write (msg .GetData ()); err != nil {
99
113
// error writing to target
100
114
targetClosed <- err
@@ -105,27 +119,59 @@ func (s *MonitorService) StreamingOpen(stream rpc.Monitor_StreamingOpenServer) e
105
119
106
120
// ...and read from the monitor and forward to the output stream
107
121
go func () {
108
- buf := make ([]byte , 8 )
122
+ dropBuffer := make ([]byte , 10240 )
123
+ dropped := 0
109
124
for {
110
- n , err := mon .Read (buf )
111
- if err != nil {
112
- // error reading from target
113
- targetClosed <- err
114
- break
115
- }
116
-
117
- if n == 0 {
118
- // target was closed
119
- targetClosed <- nil
120
- break
125
+ if bufferUsed < bufferSize {
126
+ if n , err := mon .Read (buffer [bufferUsed :]); err != nil {
127
+ // error reading from target
128
+ targetClosed <- err
129
+ break
130
+ } else if n == 0 {
131
+ // target was closed
132
+ targetClosed <- nil
133
+ break
134
+ } else {
135
+ bufferUsed += n
136
+ }
137
+ } else {
138
+ // FIXME: a very rare condition but still...
139
+ // we may be waiting here while, in the meantime, a transmit slot is
140
+ // freed: in this case the (filled) buffer will stay in the server
141
+ // until the following Read exits (-> the next char arrives from the
142
+ // monitor).
143
+
144
+ if n , err := mon .Read (dropBuffer ); err != nil {
145
+ // error reading from target
146
+ targetClosed <- err
147
+ break
148
+ } else if n == 0 {
149
+ // target was closed
150
+ targetClosed <- nil
151
+ break
152
+ } else {
153
+ dropped += n
154
+ }
121
155
}
122
156
123
- if err = stream .Send (& rpc.StreamingOpenResp {
124
- Data : buf [:n ],
125
- }); err != nil {
126
- // error sending to stream
127
- streamClosed <- err
128
- break
157
+ slots := atomic .LoadInt32 (& writeSlots )
158
+ if ! rateLimitEnabled || slots > 0 {
159
+ if err = stream .Send (& rpc.StreamingOpenResp {
160
+ Data : buffer [:bufferUsed ],
161
+ Dropped : int32 (dropped ),
162
+ }); err != nil {
163
+ // error sending to stream
164
+ streamClosed <- err
165
+ break
166
+ }
167
+ bufferUsed = 0
168
+ dropped = 0
169
+
170
+ // Rate limit, filling all the available window
171
+ if rateLimitEnabled {
172
+ slots = atomic .AddInt32 (& writeSlots , - 1 )
173
+ //fmt.Println("FREE SLOTS:", slots)
174
+ }
129
175
}
130
176
}
131
177
}()
0 commit comments