1/*
2 * Copyright (c) 2023 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#include <cstdio>
16#include <cstdlib>
17#include <cstring>
18#include <mutex>
19#include "workgroup_internal.h"
20#include "dfx/log/ffrt_log_api.h"
21#include "task_client_adapter.h"
22
23#if (defined(QOS_WORKER_FRAME_RTG) || defined(QOS_FRAME_RTG))
24constexpr int HWC_UID = 3039;
25constexpr int ROOT_UID = 0;
26constexpr int RS_RTG_ID = 10;
27
28namespace ffrt {
29static int wgId = -1;
30static WorkGroup *rsWorkGroup = nullptr;
31static int wgCount = 0;
32static std::mutex wgLock;
33
34#if (defined(QOS_WORKER_FRAME_RTG))
35
36void WorkgroupInit(struct WorkGroup* wg, uint64_t interval, int rtgId)
37{
38    wg->started = false;
39    wg->interval = interval;
40    wg->rtgId = rtgId;
41    wgId = rtgId;
42
43    for (int i = 0; i < MAX_WG_THREADS; i++) {
44        wg->tids[i] = -1;
45    }
46}
47
48int FindThreadInWorkGroup(WorkGroup *workGroup, int tid)
49{
50    if (workGroup == nullptr) {
51        FFRT_LOGE("[RSWorkGroup] find thread %{public}d in workGroup failed, workGroup is null", tid);
52        return -1;
53    }
54    for (int i = 0;i < MAX_WG_THREADS; i++) {
55        if (workGroup->tids[i] == tid) {
56            return i;
57        }
58    }
59    return -1;
60}
61
62bool InsertThreadInWorkGroup(WorkGroup *workGroup, int tid)
63{
64    if (workGroup == nullptr) {
65        FFRT_LOGE("[RSWorkGroup] join thread %{public}d into workGroup failed, workGroup is null", tid);
66        return false;
67    }
68    int targetIndex = -1;
69    for (int i = 0; i < MAX_WG_THREADS; i++) {
70        if (workGroup->tids[i] == -1) {
71            workGroup->tids[i] = tid;
72            targetIndex = i;
73            return true;
74        }
75    }
76    if (targetIndex == -1) {
77        FFRT_LOGE("[RSWorkGroup] join thread %{public}d into RSWorkGroup failed, max_thread_num: %{public}d",
78            tid, MAX_WG_THREADS);
79        return false;
80    }
81    return true;
82}
83
84WorkGroup* CreateRSWorkGroup(uint64_t interval)
85{
86    IntervalReply rs;
87    rs.rtgId = -1;
88    rs.tid = -1;
89    {
90        std::lock_guard<std::mutex> lck(wgLock);
91        if (rsWorkGroup == nullptr) {
92            CTC_QUERY_INTERVAL(QUERY_RENDER_SERVICE, rs);
93            if (rs.rtgId > 0) {
94                rsWorkGroup = new struct WorkGroup();
95                if (rsWorkGroup == nullptr) {
96                    FFRT_LOGE("[RSWorkGroup] rsWorkGroup malloc failed!");
97                    return nullptr;
98                }
99                WorkgroupInit(rsWorkGroup, interval, rs.rtgId);
100                wgCount++;
101            }
102        }
103    }
104    return rsWorkGroup;
105}
106
107bool LeaveRSWorkGroup(int tid)
108{
109    std::lock_guard<std::mutex> lck(wgLock);
110    if (rsWorkGroup == nullptr) {
111        FFRT_LOGI("[RSWorkGroup] LeaveRSWorkGroup rsWorkGroup is null ,tid:%{public}d", tid);
112        return false;
113    }
114    int existIndex = FindThreadInWorkGroup(rsWorkGroup, tid);
115    if (existIndex != -1) {
116        rsWorkGroup->tids[existIndex] = -1;
117    }
118    FFRT_LOGI("[RSWorkGroup] LeaveRSWorkGroup ,tid:%{public}d,existIndex:%{public}d", tid, existIndex);
119    return true;
120}
121
122bool JoinRSWorkGroup(int tid)
123{
124    std::lock_guard<std::mutex> lck(wgLock);
125    if (rsWorkGroup == nullptr) {
126        FFRT_LOGE("[RSWorkGroup] join thread %{public}d into RSWorkGroup failed; Create RSWorkGroup first", tid);
127        return false;
128    }
129    int existIndex = FindThreadInWorkGroup(rsWorkGroup, tid);
130    if (existIndex == -1) {
131        IntervalReply rs;
132        rs.rtgId = -1;
133        rs.tid = tid;
134        CTC_QUERY_INTERVAL(QUERY_RENDER_SERVICE, rs);
135        if (rs.rtgId > 0) {
136            bool success = InsertThreadInWorkGroup(rsWorkGroup, tid);
137            if (!success) {
138                return false;
139            }
140        }
141    }
142    FFRT_LOGI("[RSWorkGroup] update thread %{public}d success", tid);
143    return true;
144}
145
146bool DestoryRSWorkGroup()
147{
148    std::lock_guard<std::mutex> lck(wgLock);
149    if (rsWorkGroup != nullptr) {
150        delete rsWorkGroup;
151        rsWorkGroup = nullptr;
152        wgId = -1;
153        return true;
154    }
155    return false;
156}
157#endif
158
159#if defined(QOS_FRAME_RTG)
160bool JoinWG(int tid)
161{
162    if (wgId < 0) {
163        if (wgCount > 0) {
164            FFRT_LOGE("[WorkGroup] interval is unavailable");
165        }
166        return false;
167    }
168
169    int uid = getuid();
170    if (uid == RS_UID) {
171        return JoinRSWorkGroup(tid);
172    }
173    int addRet = AddThreadToRtgAdapter(tid, wgId, 0);
174    if (addRet == 0) {
175        FFRT_LOGI("[WorkGroup] update thread %{public}d success", tid);
176    } else {
177        FFRT_LOGE("[WorkGroup] update thread %{public}d failed, return %{public}d", tid, addRet);
178    }
179    return true;
180}
181
182bool LeaveWG(int tid)
183{
184    int uid = getuid();
185    if (uid == RS_UID) {
186        return LeaveRSWorkGroup(tid);
187    }
188    return false;
189}
190
191struct WorkGroup* WorkgroupCreate(uint64_t interval)
192{
193    int rtgId = -1;
194    int uid = getuid();
195    int num = 0;
196
197    if (uid == RS_UID) {
198        CreateRSWorkGroup(interval);
199        return rsWorkGroup;
200    }
201
202    if (rtgId < 0) {
203        FFRT_LOGE("[WorkGroup] create rtg group %d failed", rtgId);
204        return nullptr;
205    }
206    FFRT_LOGI("[WorkGroup] create rtg group %{public}d success", rtgId);
207
208    WorkGroup* wg = nullptr;
209    wg = new struct WorkGroup();
210    if (wg == nullptr) {
211        FFRT_LOGE("[WorkGroup] workgroup malloc failed!");
212        return nullptr;
213    }
214    WorkgroupInit(wg, interval, rtgId);
215    {
216        std::lock_guard<std::mutex> lck(wgLock);
217        wgCount++;
218    }
219    return wg;
220}
221
222int WorkgroupClear(struct WorkGroup* wg)
223{
224    int uid = getuid();
225    if (uid == RS_UID) {
226        return DestoryRSWorkGroup();
227    }
228
229    if (wg == nullptr) {
230        FFRT_LOGE("[WorkGroup] input workgroup is null");
231        return 0;
232    }
233    int ret = -1;
234    if (uid != RS_UID) {
235        ret = DestroyRtgGrpAdapter(wg->rtgId);
236        if (ret != 0) {
237            FFRT_LOGE("[WorkGroup] destroy rtg group failed");
238        } else {
239            std::lock_guard<std::mutex> lck(wgLock);
240            wgCount--;
241        }
242    }
243    delete wg;
244    wg = nullptr;
245    return ret;
246}
247
248void WorkgroupStartInterval(struct WorkGroup* wg)
249{
250    if (wg == nullptr) {
251        FFRT_LOGE("[WorkGroup] input workgroup is null");
252        return;
253    }
254
255    if (wg->started) {
256        FFRT_LOGW("[WorkGroup] already start");
257        return;
258    }
259
260    if (BeginFrameFreqAdapter(0) == 0) {
261        wg->started = true;
262    } else {
263        FFRT_LOGE("[WorkGroup] start rtg(%d) work interval failed", wg->rtgId);
264    }
265}
266
267void WorkgroupStopInterval(struct WorkGroup* wg)
268{
269    if (wg == nullptr) {
270        FFRT_LOGE("[WorkGroup] input workgroup is null");
271        return;
272    }
273
274    if (!wg->started) {
275        FFRT_LOGW("[WorkGroup] already stop");
276        return;
277    }
278
279    int ret = EndFrameFreqAdapter(0);
280    if (ret == 0) {
281        wg->started = false;
282    } else {
283        FFRT_LOGE("[WorkGroup] stop rtg(%d) work interval failed", wg->rtgId);
284    }
285}
286
287void WorkgroupJoin(struct WorkGroup* wg, int tid)
288{
289    if (wg == nullptr) {
290        FFRT_LOGE("[WorkGroup] input workgroup is null");
291        return;
292    }
293    int uid = getuid();
294    FFRT_LOGI("[WorkGroup] %s uid = %d rtgid = %d", __func__, uid, wg->rtgId);
295    if (uid == RS_UID) {
296        IntervalReply rs;
297        rs.tid = tid;
298        CTC_QUERY_INTERVAL(QUERY_RENDER_SERVICE, rs);
299        FFRT_LOGI("[WorkGroup] join thread %{public}ld", tid);
300        return;
301    }
302    int addRet = AddThreadToRtgAdapter(tid, wg->rtgId, 0);
303    if (addRet == 0) {
304        FFRT_LOGI("[WorkGroup] join thread %{public}ld success", tid);
305    } else {
306        FFRT_LOGE("[WorkGroup] join fail with %{public}d threads for %{public}d", addRet, tid);
307    }
308}
309#endif /* QOS_FRAME_RTG */
310}
311
312#endif
313