1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "softbus_conn_flow_control.h"
17 
18 #include "conn_log.h"
19 #include "softbus_adapter_mem.h"
20 #include "softbus_adapter_timer.h"
21 
22 typedef uint64_t timestamp_t;
23 struct HistoryNode {
24     ListNode node;
25 
26     timestamp_t timestamp;
27     int32_t amount;
28 };
29 
Apply(struct ConnSlideWindowController * self,int32_t expect)30 static int32_t Apply(struct ConnSlideWindowController *self, int32_t expect)
31 {
32     CONN_CHECK_AND_RETURN_RET_LOGE(self, SOFTBUS_INVALID_PARAM, CONN_COMMON, "invalid parameter, controller is null");
33 
34     int32_t status = SoftBusMutexLock(&self->lock);
35     CONN_CHECK_AND_RETURN_RET_LOGE(status == SOFTBUS_OK, SOFTBUS_LOCK_ERR, CONN_COMMON, "lock failed");
36     if (!self->active) {
37         (void)SoftBusMutexUnlock(&self->lock);
38         return expect;
39     }
40     struct HistoryNode *it = NULL;
41     struct HistoryNode *next = NULL;
42     int32_t appliedTotal = 0;
43     timestamp_t now = SoftBusGetSysTimeMs();
44     timestamp_t expiredTimestamp = now - (timestamp_t)self->windowInMillis;
45     timestamp_t currentWindowStartTimestamp = 0;
46     LIST_FOR_EACH_ENTRY_SAFE(it, next, &self->histories, struct HistoryNode, node) {
47         if (it->timestamp > expiredTimestamp) {
48             appliedTotal += it->amount;
49             currentWindowStartTimestamp = it->timestamp;
50         } else {
51             ListDelete(&it->node);
52             SoftBusFree(it);
53         }
54     }
55 
56     if (self->quotaInBytes <= appliedTotal) {
57         unsigned int sleepMs = (timestamp_t)self->windowInMillis - (now - currentWindowStartTimestamp);
58         (void)SoftBusMutexUnlock(&self->lock);
59         SoftBusSleepMs(sleepMs);
60         return Apply(self, expect);
61     }
62     int32_t remain = self->quotaInBytes - appliedTotal;
63     int32_t amount = remain > expect ? expect : remain;
64     struct HistoryNode *history = SoftBusCalloc(sizeof(*history));
65     if (history == NULL) {
66         (void)SoftBusMutexUnlock(&self->lock);
67         return expect;
68     }
69     ListInit(&history->node);
70     history->amount = amount;
71     history->timestamp = now;
72     ListAdd(&self->histories, &history->node);
73 
74     (void)SoftBusMutexUnlock(&self->lock);
75     return amount;
76 }
77 
CleanupHistoriesUnsafe(struct ConnSlideWindowController * self)78 static void CleanupHistoriesUnsafe(struct ConnSlideWindowController *self)
79 {
80     struct HistoryNode *it = NULL;
81     struct HistoryNode *next = NULL;
82     LIST_FOR_EACH_ENTRY_SAFE(it, next, &self->histories, struct HistoryNode, node) {
83         ListDelete(&it->node);
84         SoftBusFree(it);
85     }
86 }
87 
ChangeConfiguration(struct ConnSlideWindowController * self,bool active,int32_t windowInMillis,int32_t quotaInBytes)88 static int32_t ChangeConfiguration(
89     struct ConnSlideWindowController *self, bool active, int32_t windowInMillis, int32_t quotaInBytes)
90 {
91     CONN_CHECK_AND_RETURN_RET_LOGE(self, SOFTBUS_INVALID_PARAM, CONN_COMMON, "invalid parameter, controller is null");
92     if (active) {
93         CONN_CHECK_AND_RETURN_RET_LOGE(windowInMillis >= MIN_WINDOW_IN_MILLIS && windowInMillis <= MAX_WINDOW_IN_MILLIS,
94             SOFTBUS_INVALID_PARAM, CONN_COMMON, "invalid parameter, window=%u", windowInMillis);
95         CONN_CHECK_AND_RETURN_RET_LOGE(quotaInBytes >= MIN_QUOTA_IN_BYTES && quotaInBytes <= MAX_QUOTA_IN_BYTES,
96             SOFTBUS_INVALID_PARAM, CONN_COMMON, "invalid parameter, quota=%u", quotaInBytes);
97     }
98 
99     int32_t status = SoftBusMutexLock(&self->lock);
100     CONN_CHECK_AND_RETURN_RET_LOGE(status == SOFTBUS_OK, SOFTBUS_LOCK_ERR, CONN_COMMON, "lock failed");
101 
102     self->windowInMillis = windowInMillis;
103     self->quotaInBytes = quotaInBytes;
104     self->active = active;
105     // cleanup histories as configuration change
106     CleanupHistoriesUnsafe(self);
107     (void)SoftBusMutexUnlock(&self->lock);
108     return SOFTBUS_OK;
109 }
110 
Enable(struct ConnSlideWindowController * self,int32_t windowInMillis,int32_t quotaInBytes)111 static int32_t Enable(struct ConnSlideWindowController *self, int32_t windowInMillis, int32_t quotaInBytes)
112 {
113     return ChangeConfiguration(self, true, windowInMillis, quotaInBytes);
114 }
115 
Disable(struct ConnSlideWindowController * self)116 static int32_t Disable(struct ConnSlideWindowController *self)
117 {
118     return ChangeConfiguration(self, false, -1, -1);
119 }
120 
ConnSlideWindowControllerConstructor(struct ConnSlideWindowController * self)121 int32_t ConnSlideWindowControllerConstructor(struct ConnSlideWindowController *self)
122 {
123     CONN_CHECK_AND_RETURN_RET_LOGE(self, SOFTBUS_INVALID_PARAM, CONN_COMMON, "invalid parameter, controller is null");
124     int32_t ret = SoftBusMutexInit(&self->lock, NULL);
125     CONN_CHECK_AND_RETURN_RET_LOGE(ret == SOFTBUS_OK, SOFTBUS_LOCK_ERR, CONN_COMMON, "init lock failed");
126 
127     self->active = false;
128     self->windowInMillis = -1;
129     self->quotaInBytes = -1;
130     ListInit(&self->histories);
131 
132     self->apply = Apply;
133     self->enable = Enable;
134     self->disable = Disable;
135     return SOFTBUS_OK;
136 }
137 
ConnSlideWindowControllerDestructor(struct ConnSlideWindowController * self)138 void ConnSlideWindowControllerDestructor(struct ConnSlideWindowController *self)
139 {
140     CONN_CHECK_AND_RETURN_LOGE(self, CONN_COMMON, "invalid parameter, controller is null");
141     int32_t status = SoftBusMutexLock(&self->lock);
142     CONN_CHECK_AND_RETURN_LOGE(status == SOFTBUS_OK, CONN_COMMON, "lock failed");
143     CleanupHistoriesUnsafe(self);
144     SoftBusMutexDestroy(&self->lock);
145 }
146 
ConnSlideWindowControllerNew(void)147 struct ConnSlideWindowController *ConnSlideWindowControllerNew(void)
148 {
149     struct ConnSlideWindowController *controller = SoftBusCalloc(sizeof(*controller));
150     CONN_CHECK_AND_RETURN_RET_LOGE(controller, NULL, CONN_COMMON, "alloc failed");
151 
152     int32_t ret = ConnSlideWindowControllerConstructor(controller);
153     if (ret != SOFTBUS_OK) {
154         SoftBusFree(controller);
155         return NULL;
156     }
157     return controller;
158 }
159 
ConnSlideWindowControllerDelete(struct ConnSlideWindowController * self)160 void ConnSlideWindowControllerDelete(struct ConnSlideWindowController *self)
161 {
162     ConnSlideWindowControllerDestructor(self);
163     SoftBusFree(self);
164 }
165