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