1 /*
2 * Copyright (c) 2025 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_async_helper.h"
17
18 #include "softbus_adapter_mem.h"
19 #include "softbus_error_code.h"
20
21 #include "conn_log.h"
22
23 struct AsyncContext {
24 ConnAsyncFunction function;
25 void *arg;
26 };
27
28 struct CancelContext {
29 int32_t callId;
30 ConnAsyncFreeHook freeHook;
31 };
32
FreeMessageHook(SoftBusMessage * msg)33 static void FreeMessageHook(SoftBusMessage *msg)
34 {
35 CONN_CHECK_AND_RETURN_LOGE(msg != NULL, CONN_COMMON, "msg is null");
36 CONN_CHECK_AND_RETURN_LOGE(msg->obj != NULL, CONN_COMMON, "obj is null");
37
38 struct AsyncContext *ctx = (struct AsyncContext *)msg->obj;
39 SoftBusFree(ctx);
40 msg->obj = NULL;
41 SoftBusFree(msg);
42 }
43
HandleMessage(SoftBusMessage * msg)44 static void HandleMessage(SoftBusMessage *msg)
45 {
46 CONN_CHECK_AND_RETURN_LOGE(msg != NULL, CONN_COMMON, "msg is null");
47 CONN_CHECK_AND_RETURN_LOGE(msg->obj != NULL, CONN_COMMON, "obj is null");
48
49 struct AsyncContext *ctx = (struct AsyncContext *)msg->obj;
50 ctx->function(msg->what, ctx->arg);
51 // it is caller's responsibility to release 'arg'
52 ctx->arg = NULL;
53 // 'msg' and 'ctx' will be release by 'FreeMessageHook' later
54 }
55
ConnAsyncConstruct(const char * name,ConnAsync * async,SoftBusLooper * looper)56 int32_t ConnAsyncConstruct(const char *name, ConnAsync *async, SoftBusLooper *looper)
57 {
58 CONN_CHECK_AND_RETURN_RET_LOGE(name != NULL, SOFTBUS_INVALID_PARAM, CONN_COMMON, "name is null");
59 CONN_CHECK_AND_RETURN_RET_LOGE(async != NULL, SOFTBUS_INVALID_PARAM, CONN_COMMON, "async is null");
60 CONN_CHECK_AND_RETURN_RET_LOGE(looper != NULL, SOFTBUS_INVALID_PARAM, CONN_COMMON, "looper is null");
61
62 async->handler.name = (char *)name;
63 async->handler.looper = looper;
64 async->handler.HandleMessage = HandleMessage;
65
66 return SOFTBUS_OK;
67 }
68
RemoveAllAsyncCall(const SoftBusMessage * msg,void * ignore)69 static int32_t RemoveAllAsyncCall(const SoftBusMessage *msg, void *ignore)
70 {
71 (void)ignore;
72 CONN_LOGE(CONN_COMMON, "MEMORY LEAK WARNING, it should cancel before destroying, call id=%{public}d", msg->what);
73 // 0 stand for match success
74 return 0;
75 }
76
ConnAsyncDestruct(ConnAsync * async)77 void ConnAsyncDestruct(ConnAsync *async)
78 {
79 CONN_CHECK_AND_RETURN_LOGE(async != NULL, CONN_COMMON, "async is null");
80
81 SoftBusHandler *handler = &async->handler;
82 SoftBusLooper *looper = handler->looper;
83 looper->RemoveMessageCustom(looper, handler, RemoveAllAsyncCall, NULL);
84
85 async->handler.name = NULL;
86 async->handler.looper = NULL;
87 async->handler.HandleMessage = NULL;
88 }
89
ConnAsyncCall(ConnAsync * async,ConnAsyncFunction function,void * arg,uint64_t delayMs)90 int32_t ConnAsyncCall(ConnAsync *async, ConnAsyncFunction function, void *arg, uint64_t delayMs)
91 {
92 static uint16_t callIdGenerator = 0;
93
94 CONN_CHECK_AND_RETURN_RET_LOGE(async != NULL, SOFTBUS_INVALID_PARAM, CONN_COMMON, "async is null");
95 CONN_CHECK_AND_RETURN_RET_LOGE(function != NULL, SOFTBUS_INVALID_PARAM, CONN_COMMON, "function is null");
96 // arg is nullable
97
98 int32_t callId = (++callIdGenerator);
99 struct AsyncContext *ctx = SoftBusCalloc(sizeof(struct AsyncContext));
100 CONN_CHECK_AND_RETURN_RET_LOGE(ctx, SOFTBUS_MEM_ERR, CONN_COMMON, "malloc async ctx fail");
101 ctx->function = function;
102 ctx->arg = arg;
103
104 SoftBusMessage *msg = SoftBusCalloc(sizeof(SoftBusMessage));
105 if (msg == NULL) {
106 CONN_LOGE(CONN_COMMON, "malloc softbus message fail");
107 SoftBusFree(ctx);
108 return SOFTBUS_MEM_ERR;
109 }
110 msg->what = callId;
111 msg->obj = ctx;
112 msg->handler = &async->handler;
113 msg->FreeMessage = FreeMessageHook;
114
115 SoftBusLooper *looper = async->handler.looper;
116 looper->PostMessageDelay(looper, msg, delayMs);
117
118 CONN_LOGI(CONN_COMMON, "receive async call, call id=%{public}d, delay=%{public}" PRIu64 "ms", callId, delayMs);
119 return callId;
120 }
121
FreeAsyncCallArg(const SoftBusMessage * msg,void * args)122 static int32_t FreeAsyncCallArg(const SoftBusMessage *msg, void *args)
123 {
124 struct CancelContext *cancelCtx = (struct CancelContext *)args;
125 if (msg->what != cancelCtx->callId) {
126 // 1 stand for mismatch
127 return 1;
128 }
129 struct AsyncContext *asyncCtx = (struct AsyncContext *)msg->obj;
130 if (asyncCtx->arg != NULL) {
131 if (cancelCtx->freeHook != NULL) {
132 cancelCtx->freeHook(asyncCtx->arg);
133 asyncCtx->arg = NULL;
134 } else {
135 CONN_LOGE(CONN_COMMON, "MEMORY LEAK WARNING, it should provide hook to free memory, call id=%{public}d",
136 msg->what);
137 }
138 }
139 // 0 stand for match success
140 return 0;
141 }
142
ConnAsyncCancel(ConnAsync * async,int32_t callId,ConnAsyncFreeHook hook)143 void ConnAsyncCancel(ConnAsync *async, int32_t callId, ConnAsyncFreeHook hook)
144 {
145 CONN_CHECK_AND_RETURN_LOGE(async != NULL, CONN_COMMON, "async is null");
146 // free hook is nullable
147
148 CONN_LOGI(CONN_COMMON, "cancel async call, call id=%{public}d", callId);
149 struct CancelContext ctx = {
150 .callId = callId,
151 .freeHook = hook,
152 };
153 SoftBusHandler *handler = &async->handler;
154 SoftBusLooper *looper = handler->looper;
155 looper->RemoveMessageCustom(looper, handler, FreeAsyncCallArg, &ctx);
156 }
157
ConnAsyncGetInstance(void)158 ConnAsync *ConnAsyncGetInstance(void)
159 {
160 static ConnAsync async = { 0 };
161 return &async;
162 }
163
ConnAsyncInit(void)164 int32_t ConnAsyncInit(void)
165 {
166 SoftBusLooper *looper = GetLooper(LOOP_TYPE_CONN);
167 CONN_CHECK_AND_RETURN_RET_LOGE(
168 looper, SOFTBUS_INVALID_PARAM, CONN_COMMON, "connection looper is null, init looper module first");
169
170 ConnAsync *async = ConnAsyncGetInstance();
171 return ConnAsyncConstruct("conn_async", async, looper);
172 }
173