1 /*
2 * Copyright (C) 2021-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 "ipc_thread_skeleton.h"
17
18 #include <cinttypes>
19 #include <memory>
20 #include <sys/prctl.h>
21 #include <sys/syscall.h>
22
23 #include "binder_invoker.h"
24 #include "ffrt.h"
25 #include "hilog/log_c.h"
26 #include "hilog/log_cpp.h"
27 #include "invoker_factory.h"
28 #include "ipc_debug.h"
29 #include "ipc_object_proxy.h"
30 #include "iremote_invoker.h"
31 #include "iremote_object.h"
32 #include "log_tags.h"
33 #include "new"
34 #include "process_skeleton.h"
35 #include "pthread.h"
36
37 namespace OHOS {
38 #ifdef CONFIG_IPC_SINGLE
39 namespace IPC_SINGLE {
40 #endif
41 using namespace OHOS::HiviewDFX;
42 pthread_key_t IPCThreadSkeleton::TLSKey_ = 0;
43 pthread_once_t IPCThreadSkeleton::TLSKeyOnce_ = PTHREAD_ONCE_INIT;
44
45 static constexpr uint32_t MAX_THREAD_NAME_LEN = 20;
46 static constexpr HiLogLabel LOG_LABEL = { LOG_CORE, LOG_ID_IPC_THREAD_SKELETON, "IPCThreadSkeleton" };
47
DeleteTlsKey()48 extern "C" __attribute__((destructor)) void DeleteTlsKey()
49 {
50 pthread_key_t key = IPCThreadSkeleton::GetTlsKey();
51 pthread_key_delete(key);
52 }
53
TlsDestructor(void * args)54 void IPCThreadSkeleton::TlsDestructor(void *args)
55 {
56 auto *current = static_cast<IPCThreadSkeleton *>(args);
57 if (current == nullptr) {
58 ZLOGE(LOG_LABEL, "current is nullptr");
59 return;
60 }
61
62 uint32_t ret = current->usingFlag_.load();
63 if ((ret != INVOKER_USE_MAGIC) && (ret != INVOKER_IDLE_MAGIC)) {
64 ZLOGF(LOG_LABEL, "memory may be damaged, ret:%{public}u", ret);
65 return;
66 }
67
68 uint32_t itemIndex = static_cast<uint32_t>(IRemoteObject::IF_PROT_BINDER);
69 if (itemIndex < IPCThreadSkeleton::INVOKER_MAX_COUNT && current->invokers_[itemIndex] != nullptr) {
70 BinderInvoker *invoker = reinterpret_cast<BinderInvoker *>(current->invokers_[itemIndex]);
71 invoker->FlushCommands(nullptr);
72 invoker->ExitCurrentThread();
73 }
74 delete current;
75 }
76
MakeTlsKey()77 void IPCThreadSkeleton::MakeTlsKey()
78 {
79 auto ret = pthread_key_create(&TLSKey_, IPCThreadSkeleton::TlsDestructor);
80 if (ret != 0) {
81 ZLOGE(LOG_LABEL, "pthread_key_create fail, ret:%{public}d", ret);
82 return;
83 }
84 ZLOGD(LOG_LABEL, "key:%{public}d", TLSKey_);
85 }
86
GetVaildInstance(IPCThreadSkeleton * & instance)87 void IPCThreadSkeleton::GetVaildInstance(IPCThreadSkeleton *&instance)
88 {
89 if (instance == nullptr) {
90 ZLOGE(LOG_LABEL, "instance is null");
91 return;
92 }
93
94 // 1. a FFRT task may be executed on multiple threads in different time periods.
95 // 2. a thread can executed multiple FFRT tasks in different time periods.
96 auto tid = gettid();
97 auto taskId = ffrt_this_task_get_id();
98 if (tid != instance->tid_ && taskId != instance->ffrtTaskId_) {
99 ZLOGE(LOG_LABEL, "TLS mismatch, curTid:%{public}d tlsTid:%{public}d, curTaskId:%{public}" PRIu64
100 " tlsTaskId:%{public}" PRIu64 ", key:%{public}u instance:%{public}u threadName:%{public}s",
101 tid, instance->tid_, taskId, instance->ffrtTaskId_, TLSKey_, ProcessSkeleton::ConvertAddr(instance),
102 instance->threadName_.c_str());
103 pthread_setspecific(TLSKey_, nullptr);
104 instance = new (std::nothrow) IPCThreadSkeleton();
105 }
106 }
107
SaveThreadName(const std::string & name)108 void IPCThreadSkeleton::SaveThreadName(const std::string &name)
109 {
110 IPCThreadSkeleton *current = IPCThreadSkeleton::GetCurrent();
111 if (current == nullptr) {
112 return;
113 }
114 if (IsInstanceException(current->exitFlag_)) {
115 return;
116 }
117 current->threadName_ = name;
118 }
119
GetCurrent()120 IPCThreadSkeleton *IPCThreadSkeleton::GetCurrent()
121 {
122 pthread_once(&TLSKeyOnce_, IPCThreadSkeleton::MakeTlsKey);
123
124 IPCThreadSkeleton *current = nullptr;
125 void *curTLS = pthread_getspecific(TLSKey_);
126 if (curTLS != nullptr) {
127 current = reinterpret_cast<IPCThreadSkeleton *>(curTLS);
128 if (IsInstanceException(current->exitFlag_)) {
129 return nullptr;
130 }
131 GetVaildInstance(current);
132 } else {
133 current = new (std::nothrow) IPCThreadSkeleton();
134 }
135 return current;
136 }
137
IPCThreadSkeleton()138 IPCThreadSkeleton::IPCThreadSkeleton() : tid_(gettid()), ffrtTaskId_(ffrt_this_task_get_id())
139 {
140 pthread_setspecific(TLSKey_, this);
141 char name[MAX_THREAD_NAME_LEN] = { 0 };
142 auto ret = prctl(PR_GET_NAME, name);
143 if (ret != 0) {
144 ZLOGW(LOG_LABEL, "get thread name fail, tid:%{public}d ret:%{public}d", tid_, ret);
145 return;
146 }
147 threadName_ = name;
148 ZLOGD(LOG_LABEL, "instance:%{public}u name:%{public}s", ProcessSkeleton::ConvertAddr(this), threadName_.c_str());
149 }
150
~IPCThreadSkeleton()151 IPCThreadSkeleton::~IPCThreadSkeleton()
152 {
153 exitFlag_ = INVOKER_IDLE_MAGIC;
154 pthread_setspecific(TLSKey_, nullptr);
155 uint32_t ret = usingFlag_.load();
156 while (ret == INVOKER_USE_MAGIC) {
157 ZLOGI(LOG_LABEL, "%{public}u is using, wait a moment", ProcessSkeleton::ConvertAddr(this));
158 usleep(1);
159 ret = usingFlag_.load();
160 }
161 if ((ret != INVOKER_USE_MAGIC) && (ret != INVOKER_IDLE_MAGIC)) {
162 ZLOGF(LOG_LABEL, "memory may be damaged, ret:%{public}u", ret);
163 return;
164 }
165
166 for (auto &invoker : invokers_) {
167 delete invoker;
168 invoker = nullptr;
169 }
170 if (threadType_ == ThreadType::IPC_THREAD) {
171 // subtract thread count when thread exiting
172 auto process = ProcessSkeleton::GetInstance();
173 if (process != nullptr) {
174 process->DecreaseThreadCount();
175 }
176 }
177 ZLOGD(LOG_LABEL, "thread exit, instance:%{public}u name:%{public}s threadType:%{public}d",
178 ProcessSkeleton::ConvertAddr(this), threadName_.c_str(), threadType_);
179 }
180
IsInstanceException(std::atomic<uint32_t> & flag)181 bool IPCThreadSkeleton::IsInstanceException(std::atomic<uint32_t> &flag)
182 {
183 if (flag == INVOKER_USE_MAGIC) {
184 return false;
185 }
186
187 if (flag == INVOKER_IDLE_MAGIC) {
188 ZLOGE(LOG_LABEL, "Instance is exiting");
189 return true;
190 }
191
192 ZLOGE(LOG_LABEL, "Memory may be damaged, flag:%{public}u", flag.load());
193 return true;
194 }
195
GetRemoteInvoker(int proto)196 IRemoteInvoker *IPCThreadSkeleton::GetRemoteInvoker(int proto)
197 {
198 if (proto < 0 || static_cast<uint32_t>(proto) >= IPCThreadSkeleton::INVOKER_MAX_COUNT) {
199 ZLOGE(LOG_LABEL, "invalid proto:%{public}d", proto);
200 return nullptr;
201 }
202
203 IPCThreadSkeleton *current = IPCThreadSkeleton::GetCurrent();
204 if (current == nullptr) {
205 return nullptr;
206 }
207 if (IsInstanceException(current->exitFlag_)) {
208 return nullptr;
209 }
210
211 if ((current->usingFlag_ != INVOKER_USE_MAGIC) && (current->usingFlag_ != INVOKER_IDLE_MAGIC)) {
212 ZLOGF(LOG_LABEL, "memory may be damaged, flag:%{public}u", current->usingFlag_.load());
213 return nullptr;
214 }
215 current->usingFlag_ = INVOKER_USE_MAGIC;
216 IRemoteInvoker *invoker = nullptr;
217 auto it = current->invokers_[proto];
218 if (it != nullptr) {
219 invoker = it;
220 } else {
221 InvokerFactory &factory = InvokerFactory::Get();
222 invoker = factory.newInstance(proto);
223 if (invoker == nullptr) {
224 current->usingFlag_ = INVOKER_IDLE_MAGIC;
225 uint64_t curTime = static_cast<uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(
226 std::chrono::steady_clock::now().time_since_epoch()).count());
227 ZLOGE(LOG_LABEL, "invoker is NULL, proto:%{public}d time:%{public}" PRIu64, proto, curTime);
228 return nullptr;
229 }
230
231 // non-thread safe, add lock to protect it.
232 current->invokers_[proto] = invoker;
233 }
234
235 current->usingFlag_ = INVOKER_IDLE_MAGIC;
236 return invoker;
237 }
238
GetActiveInvoker()239 IRemoteInvoker *IPCThreadSkeleton::GetActiveInvoker()
240 {
241 IRemoteInvoker *binderInvoker = IPCThreadSkeleton::GetRemoteInvoker(IRemoteObject::IF_PROT_BINDER);
242 if ((binderInvoker != nullptr) && (binderInvoker->GetStatus() == IRemoteInvoker::ACTIVE_INVOKER)) {
243 return binderInvoker;
244 }
245 #ifndef CONFIG_IPC_SINGLE
246 IRemoteInvoker *dbinderInvoker = IPCThreadSkeleton::GetRemoteInvoker(IRemoteObject::IF_PROT_DATABUS);
247 if ((dbinderInvoker != nullptr) && (dbinderInvoker->GetStatus() == IRemoteInvoker::ACTIVE_INVOKER)) {
248 return dbinderInvoker;
249 }
250 #endif
251 return nullptr;
252 }
253
GetProxyInvoker(IRemoteObject * object)254 IRemoteInvoker *IPCThreadSkeleton::GetProxyInvoker(IRemoteObject *object)
255 {
256 if (object == nullptr) {
257 ZLOGE(LOG_LABEL, "proxy is invalid");
258 return nullptr;
259 }
260 if (!object->IsProxyObject()) {
261 return nullptr;
262 }
263
264 IPCObjectProxy *proxy = reinterpret_cast<IPCObjectProxy *>(object);
265 return IPCThreadSkeleton::GetRemoteInvoker(proxy->GetProto());
266 }
267
GetDefaultInvoker()268 IRemoteInvoker *IPCThreadSkeleton::GetDefaultInvoker()
269 {
270 return GetRemoteInvoker(IRemoteObject::IF_PROT_DEFAULT);
271 }
272
GetTlsKey()273 pthread_key_t IPCThreadSkeleton::GetTlsKey()
274 {
275 return TLSKey_;
276 }
277
JoinWorkThread(int proto)278 void IPCThreadSkeleton::JoinWorkThread(int proto)
279 {
280 IRemoteInvoker *invoker = GetRemoteInvoker(proto);
281 if (invoker != nullptr) {
282 invoker->JoinThread(true);
283 }
284 }
285
StopWorkThread(int proto)286 void IPCThreadSkeleton::StopWorkThread(int proto)
287 {
288 IRemoteInvoker *invoker = GetRemoteInvoker(proto);
289 if (invoker != nullptr) {
290 invoker->StopWorkThread();
291 }
292 }
293
UpdateSendRequestCount(int delta)294 bool IPCThreadSkeleton::UpdateSendRequestCount(int delta)
295 {
296 IPCThreadSkeleton *current = IPCThreadSkeleton::GetCurrent();
297 if (current == nullptr) {
298 return false;
299 }
300 if (IsInstanceException(current->exitFlag_)) {
301 return false;
302 }
303 current->sendRequestCount_ += delta;
304 return true;
305 }
306
IsSendRequesting()307 bool IPCThreadSkeleton::IsSendRequesting()
308 {
309 return sendRequestCount_ > 0;
310 }
311
SetThreadType(ThreadType type)312 bool IPCThreadSkeleton::SetThreadType(ThreadType type)
313 {
314 IPCThreadSkeleton *current = IPCThreadSkeleton::GetCurrent();
315 if (current == nullptr) {
316 ZLOGE(LOG_LABEL, "IPCThreadSkeleton current get failed");
317 return false;
318 }
319
320 current->threadType_ = type;
321 return true;
322 }
323
GetThreadType()324 ThreadType IPCThreadSkeleton::GetThreadType()
325 {
326 IPCThreadSkeleton *current = IPCThreadSkeleton::GetCurrent();
327 if (current == nullptr) {
328 ZLOGE(LOG_LABEL, "IPCThreadSkeleton current get failed");
329 return ThreadType::NORMAL_THREAD;
330 }
331 return current->threadType_;
332 }
333 #ifdef CONFIG_IPC_SINGLE
334 } // namespace IPC_SINGLE
335 #endif
336 } // namespace OHOS
337