1 /*
2     SPDX-FileCopyrightText: 2005 Joris Guisson <joris.guisson@gmail.com>
3 
4     SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 #include "socketgroup.h"
7 #include "trafficshapedsocket.h"
8 #include <math.h>
9 #include <util/functions.h>
10 #include <util/log.h>
11 
12 using namespace bt;
13 
14 namespace net
15 {
SocketGroup(Uint32 limit,Uint32 assured_rate)16 SocketGroup::SocketGroup(Uint32 limit, Uint32 assured_rate)
17     : limit(limit)
18     , assured_rate(assured_rate)
19 {
20     prev_run_time = bt::CurrentTime();
21     group_allowance = 0;
22     group_assured = 0;
23 }
24 
~SocketGroup()25 SocketGroup::~SocketGroup()
26 {
27 }
28 
processUnlimited(bool up,bt::TimeStamp now)29 void SocketGroup::processUnlimited(bool up, bt::TimeStamp now)
30 {
31     std::list<TrafficShapedSocket *>::iterator i = sockets.begin();
32     while (i != sockets.end()) {
33         TrafficShapedSocket *s = *i;
34         if (s) {
35             if (up)
36                 s->write(0, now);
37             else
38                 s->read(0, now);
39         }
40         ++i;
41     }
42 }
43 
processLimited(bool up,bt::TimeStamp now,Uint32 & allowance)44 bool SocketGroup::processLimited(bool up, bt::TimeStamp now, Uint32 &allowance)
45 {
46     Uint32 bslot = allowance / sockets.size() + 1;
47 
48     std::list<TrafficShapedSocket *>::iterator itr = sockets.begin();
49 
50     // while we can send and there are sockets left to send
51     while (sockets.size() > 0 && allowance > 0) {
52         Uint32 as = bslot;
53         if (as > allowance)
54             as = allowance;
55 
56         TrafficShapedSocket *s = *itr;
57         if (s) {
58             Uint32 ret = 0;
59             if (up)
60                 ret = s->write(as, now);
61             else
62                 ret = s->read(as, now);
63 
64             // if this socket did what it was supposed to do,
65             // it can have another go if stuff is leftover
66             // if it doesn't, we erase it from the list
67             if (ret != as)
68                 itr = sockets.erase(itr);
69             else
70                 ++itr;
71 
72             if (ret > allowance)
73                 allowance = 0;
74             else
75                 allowance -= ret;
76         } else {
77             // 0 pointer so just erase
78             itr = sockets.erase(itr);
79         }
80 
81         // wrap around if necessary
82         if (itr == sockets.end())
83             itr = sockets.begin();
84     }
85 
86     return sockets.size() > 0;
87 }
88 
download(Uint32 & global_allowance,bt::TimeStamp now)89 bool SocketGroup::download(Uint32 &global_allowance, bt::TimeStamp now)
90 {
91     return process(false, now, global_allowance);
92 }
93 
upload(Uint32 & global_allowance,bt::TimeStamp now)94 bool SocketGroup::upload(Uint32 &global_allowance, bt::TimeStamp now)
95 {
96     return process(true, now, global_allowance);
97 }
98 
calcAllowance(bt::TimeStamp now)99 void SocketGroup::calcAllowance(bt::TimeStamp now)
100 {
101     if (limit > 0)
102         group_allowance = (Uint32)ceil(1.0 * limit * (now - prev_run_time) * 0.001);
103     else
104         group_allowance = 0;
105 
106     if (assured_rate > 0)
107         group_assured = (Uint32)ceil(1.0 * assured_rate * (now - prev_run_time) * 0.001);
108     else
109         group_assured = 0;
110 
111     prev_run_time = now;
112 }
113 
process(bool up,bt::TimeStamp now,Uint32 & global_allowance)114 bool SocketGroup::process(bool up, bt::TimeStamp now, Uint32 &global_allowance)
115 {
116     if (limit > 0) {
117         if (group_allowance == 0) {
118             clear();
119             return false;
120         }
121 
122         bool ret = false;
123         if (global_allowance == 0) {
124             Uint32 p = group_allowance;
125             ret = processLimited(up, now, p);
126             group_allowance = p;
127         } else if (global_allowance <= group_allowance) {
128             Uint32 tmp = global_allowance;
129             ret = processLimited(up, now, tmp);
130 
131             Uint32 done = (global_allowance - tmp);
132             if (group_allowance < done)
133                 group_allowance = 0;
134             else
135                 group_allowance -= done;
136 
137             global_allowance = tmp;
138         } else {
139             Uint32 p = group_allowance;
140             ret = processLimited(up, now, p);
141 
142             Uint32 done = (group_allowance - p);
143             if (global_allowance < done)
144                 global_allowance = 0;
145             else
146                 global_allowance -= done;
147 
148             group_allowance = p;
149         }
150 
151         // if group allowance is used up, this group can no longer do anything
152         if (group_allowance == 0) {
153             clear();
154             return false;
155         } else
156             return ret;
157     } else if (global_allowance > 0) {
158         return processLimited(up, now, global_allowance);
159     } else {
160         processUnlimited(up, now);
161         return false;
162     }
163 }
164 
165 }
166