• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
77 // LCOV_EXCL_START
MakeTlsKey()78 void IPCThreadSkeleton::MakeTlsKey()
79 {
80     auto ret = pthread_key_create(&TLSKey_, IPCThreadSkeleton::TlsDestructor);
81     if (ret != 0) {
82         ZLOGE(LOG_LABEL, "pthread_key_create fail, ret:%{public}d", ret);
83         return;
84     }
85     ZLOGD(LOG_LABEL, "key:%{public}d", TLSKey_);
86 }
87 // LCOV_EXCL_STOP
88 
GetVaildInstance(IPCThreadSkeleton * & instance)89 void IPCThreadSkeleton::GetVaildInstance(IPCThreadSkeleton *&instance)
90 {
91     if (instance == nullptr) {
92         ZLOGE(LOG_LABEL, "instance is null");
93         return;
94     }
95 
96     // 1. a FFRT task may be executed on multiple threads in different time periods.
97     // 2. a thread can executed multiple FFRT tasks in different time periods.
98     auto tid = gettid();
99     auto taskId = ffrt_this_task_get_id();
100     if (tid != instance->tid_ && taskId != instance->ffrtTaskId_) {
101         ZLOGE(LOG_LABEL, "TLS mismatch, curTid:%{public}d tlsTid:%{public}d, curTaskId:%{public}" PRIu64
102             " tlsTaskId:%{public}" PRIu64 ", key:%{public}u instance:%{public}u threadName:%{public}s",
103             tid, instance->tid_, taskId, instance->ffrtTaskId_, TLSKey_, ProcessSkeleton::ConvertAddr(instance),
104             instance->threadName_.c_str());
105         pthread_setspecific(TLSKey_, nullptr);
106         instance = new (std::nothrow) IPCThreadSkeleton();
107     }
108 }
109 
SaveThreadName(const std::string & name)110 void IPCThreadSkeleton::SaveThreadName(const std::string &name)
111 {
112     IPCThreadSkeleton *current = IPCThreadSkeleton::GetCurrent();
113     if (current == nullptr) {
114         return;
115     }
116     if (IsInstanceException(current->exitFlag_)) {
117         return;
118     }
119     current->threadName_ = name;
120 }
121 
122 // LCOV_EXCL_START
GetCurrent()123 IPCThreadSkeleton *IPCThreadSkeleton::GetCurrent()
124 {
125     pthread_once(&TLSKeyOnce_, IPCThreadSkeleton::MakeTlsKey);
126 
127     IPCThreadSkeleton *current = nullptr;
128     void *curTLS = pthread_getspecific(TLSKey_);
129     if (curTLS != nullptr) {
130         current = reinterpret_cast<IPCThreadSkeleton *>(curTLS);
131         if (IsInstanceException(current->exitFlag_)) {
132             return nullptr;
133         }
134         GetVaildInstance(current);
135     } else {
136         current = new (std::nothrow) IPCThreadSkeleton();
137     }
138     return current;
139 }
140 // LCOV_EXCL_STOP
141 
142 // LCOV_EXCL_START
IPCThreadSkeleton()143 IPCThreadSkeleton::IPCThreadSkeleton() : tid_(gettid()), ffrtTaskId_(ffrt_this_task_get_id())
144 {
145     pthread_setspecific(TLSKey_, this);
146     char name[MAX_THREAD_NAME_LEN] = { 0 };
147     auto ret = prctl(PR_GET_NAME, name);
148     if (ret != 0) {
149         ZLOGW(LOG_LABEL, "get thread name fail, tid:%{public}d ret:%{public}d", tid_, ret);
150         return;
151     }
152     threadName_ = name;
153     ZLOGD(LOG_LABEL, "instance:%{public}u name:%{public}s", ProcessSkeleton::ConvertAddr(this), threadName_.c_str());
154 }
155 // LCOV_EXCL_STOP
156 
157 // LCOV_EXCL_START
~IPCThreadSkeleton()158 IPCThreadSkeleton::~IPCThreadSkeleton()
159 {
160     exitFlag_ = INVOKER_IDLE_MAGIC;
161     pthread_setspecific(TLSKey_, nullptr);
162     uint32_t ret = usingFlag_.load();
163     while (ret == INVOKER_USE_MAGIC) {
164         ZLOGI(LOG_LABEL, "%{public}u is using, wait a moment", ProcessSkeleton::ConvertAddr(this));
165         usleep(1);
166         ret = usingFlag_.load();
167     }
168     if ((ret != INVOKER_USE_MAGIC) && (ret != INVOKER_IDLE_MAGIC)) {
169         ZLOGF(LOG_LABEL, "memory may be damaged, ret:%{public}u", ret);
170         return;
171     }
172 
173     for (auto &invoker : invokers_) {
174         delete invoker;
175         invoker = nullptr;
176     }
177     if (threadType_ == ThreadType::IPC_THREAD) {
178         // subtract thread count when thread exiting
179         auto process = ProcessSkeleton::GetInstance();
180         if (process != nullptr) {
181             process->DecreaseThreadCount();
182         }
183     }
184     ZLOGD(LOG_LABEL, "thread exit, instance:%{public}u name:%{public}s threadType:%{public}d",
185         ProcessSkeleton::ConvertAddr(this), threadName_.c_str(), threadType_);
186 }
187 // LCOV_EXCL_STOP
188 
IsInstanceException(std::atomic<uint32_t> & flag)189 bool IPCThreadSkeleton::IsInstanceException(std::atomic<uint32_t> &flag)
190 {
191     if (flag == INVOKER_USE_MAGIC) {
192         return false;
193     }
194 
195     if (flag == INVOKER_IDLE_MAGIC) {
196         ZLOGE(LOG_LABEL, "Instance is exiting");
197         return true;
198     }
199 
200     ZLOGE(LOG_LABEL, "Memory may be damaged, flag:%{public}u", flag.load());
201     return true;
202 }
203 
GetRemoteInvoker(int proto)204 IRemoteInvoker *IPCThreadSkeleton::GetRemoteInvoker(int proto)
205 {
206     if (proto < 0 || static_cast<uint32_t>(proto) >= IPCThreadSkeleton::INVOKER_MAX_COUNT) {
207         ZLOGE(LOG_LABEL, "invalid proto:%{public}d", proto);
208         return nullptr;
209     }
210 
211     IPCThreadSkeleton *current = IPCThreadSkeleton::GetCurrent();
212     if (current == nullptr) {
213         return nullptr;
214     }
215     if (IsInstanceException(current->exitFlag_)) {
216         return nullptr;
217     }
218 
219     if ((current->usingFlag_ != INVOKER_USE_MAGIC) && (current->usingFlag_ != INVOKER_IDLE_MAGIC)) {
220         ZLOGF(LOG_LABEL, "memory may be damaged, flag:%{public}u", current->usingFlag_.load());
221         return nullptr;
222     }
223     current->usingFlag_ = INVOKER_USE_MAGIC;
224     IRemoteInvoker *invoker = nullptr;
225     auto it = current->invokers_[proto];
226     if (it != nullptr) {
227         invoker = it;
228     } else {
229         InvokerFactory &factory = InvokerFactory::Get();
230         invoker = factory.newInstance(proto);
231         if (invoker == nullptr) {
232             current->usingFlag_ = INVOKER_IDLE_MAGIC;
233             uint64_t curTime = static_cast<uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(
234                 std::chrono::steady_clock::now().time_since_epoch()).count());
235             ZLOGE(LOG_LABEL, "invoker is NULL, proto:%{public}d time:%{public}" PRIu64, proto, curTime);
236             return nullptr;
237         }
238 
239         // non-thread safe, add lock to protect it.
240         current->invokers_[proto] = invoker;
241     }
242 
243     current->usingFlag_ = INVOKER_IDLE_MAGIC;
244     return invoker;
245 }
246 
247 // LCOV_EXCL_START
GetActiveInvoker()248 IRemoteInvoker *IPCThreadSkeleton::GetActiveInvoker()
249 {
250     IRemoteInvoker *binderInvoker = IPCThreadSkeleton::GetRemoteInvoker(IRemoteObject::IF_PROT_BINDER);
251     if ((binderInvoker != nullptr) && (binderInvoker->GetStatus() == IRemoteInvoker::ACTIVE_INVOKER)) {
252         return binderInvoker;
253     }
254 #ifndef CONFIG_IPC_SINGLE
255     IRemoteInvoker *dbinderInvoker = IPCThreadSkeleton::GetRemoteInvoker(IRemoteObject::IF_PROT_DATABUS);
256     if ((dbinderInvoker != nullptr) && (dbinderInvoker->GetStatus() == IRemoteInvoker::ACTIVE_INVOKER)) {
257         return dbinderInvoker;
258     }
259 #endif
260     return nullptr;
261 }
262 // LCOV_EXCL_STOP
263 
GetProxyInvoker(IRemoteObject * object)264 IRemoteInvoker *IPCThreadSkeleton::GetProxyInvoker(IRemoteObject *object)
265 {
266     if (object == nullptr) {
267         ZLOGE(LOG_LABEL, "proxy is invalid");
268         return nullptr;
269     }
270     if (!object->IsProxyObject()) {
271         return nullptr;
272     }
273 
274     IPCObjectProxy *proxy = reinterpret_cast<IPCObjectProxy *>(object);
275     return IPCThreadSkeleton::GetRemoteInvoker(proxy->GetProto());
276 }
277 
GetDefaultInvoker()278 IRemoteInvoker *IPCThreadSkeleton::GetDefaultInvoker()
279 {
280     return GetRemoteInvoker(IRemoteObject::IF_PROT_DEFAULT);
281 }
282 
GetTlsKey()283 pthread_key_t IPCThreadSkeleton::GetTlsKey()
284 {
285     return TLSKey_;
286 }
287 
JoinWorkThread(int proto)288 void IPCThreadSkeleton::JoinWorkThread(int proto)
289 {
290     IRemoteInvoker *invoker = GetRemoteInvoker(proto);
291     if (invoker != nullptr) {
292         invoker->JoinThread(true);
293     }
294 }
295 
StopWorkThread(int proto)296 void IPCThreadSkeleton::StopWorkThread(int proto)
297 {
298     IRemoteInvoker *invoker = GetRemoteInvoker(proto);
299     if (invoker != nullptr) {
300         invoker->StopWorkThread();
301     }
302 }
303 
UpdateSendRequestCount(int delta)304 bool IPCThreadSkeleton::UpdateSendRequestCount(int delta)
305 {
306     IPCThreadSkeleton *current = IPCThreadSkeleton::GetCurrent();
307     if (current == nullptr) {
308         return false;
309     }
310     if (IsInstanceException(current->exitFlag_)) {
311         return false;
312     }
313     current->sendRequestCount_ += delta;
314     return true;
315 }
316 
317 // LCOV_EXCL_START
IsSendRequesting()318 bool IPCThreadSkeleton::IsSendRequesting()
319 {
320     return sendRequestCount_ > 0;
321 }
322 // LCOV_EXCL_STOP
323 
SetThreadType(ThreadType type)324 bool IPCThreadSkeleton::SetThreadType(ThreadType type)
325 {
326     IPCThreadSkeleton *current = IPCThreadSkeleton::GetCurrent();
327     if (current == nullptr) {
328         ZLOGE(LOG_LABEL, "IPCThreadSkeleton current get failed");
329         return false;
330     }
331 
332     current->threadType_ = type;
333     return true;
334 }
335 
336 // LCOV_EXCL_START
GetThreadType()337 ThreadType IPCThreadSkeleton::GetThreadType()
338 {
339     IPCThreadSkeleton *current = IPCThreadSkeleton::GetCurrent();
340     if (current == nullptr) {
341         ZLOGE(LOG_LABEL, "IPCThreadSkeleton current get failed");
342         return ThreadType::NORMAL_THREAD;
343     }
344     return current->threadType_;
345 }
346 // LCOV_EXCL_STOP
347 
348 // LCOV_EXCL_START
GetThreadInvocationState()349 int32_t IPCThreadSkeleton::GetThreadInvocationState()
350 {
351     IRemoteInvoker *invoker = IPCThreadSkeleton::GetRemoteInvoker(IRemoteObject::IF_PROT_BINDER);
352     if (invoker == nullptr) {
353         ZLOGE(LOG_LABEL, "get remote invoker failed");
354         return STATUS_UNKNOWN;
355     }
356     if(invoker->GetStatus() != IRemoteInvoker::ACTIVE_INVOKER) {
357         ZLOGE(LOG_LABEL, "not ipc thread");
358         return STATUS_NO_SUPPORT;
359     }
360     BinderInvoker *binder = reinterpret_cast<BinderInvoker *>(invoker);
361     return binder->GetInvocationState();
362 }
363 // LCOV_EXCL_STOP
364 #ifdef CONFIG_IPC_SINGLE
365 } // namespace IPC_SINGLE
366 #endif
367 } // namespace OHOS
368