1 /**
2 * Copyright (c) 2021-2022 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 #ifndef LIBPANDABASE_OS_THREAD_H
17 #define LIBPANDABASE_OS_THREAD_H
18
19 #include "os/error.h"
20 #include "utils/expected.h"
21
22 #include <cstdint>
23 #include <memory>
24 #include <thread>
25 #include <pthread.h>
26 #ifdef PANDA_TARGET_UNIX
27 #include "platforms/unix/libpandabase/thread.h"
28 #elif defined PANDA_TARGET_WINDOWS
29 #include "platforms/windows/libpandabase/thread.h"
30 #else
31 #error "Unsupported platform"
32 #endif
33
34 namespace panda::os::thread {
35
36 using ThreadId = uint32_t;
37 using native_handle_type = std::thread::native_handle_type;
38
39 WEAK_FOR_LTO_START
40
41 ThreadId GetCurrentThreadId();
42 int GetPid();
43 int SetThreadName(native_handle_type pthread_handle, const char *name);
44 native_handle_type GetNativeHandle();
45 void NativeSleep(unsigned int ms);
46 void ThreadDetach(native_handle_type pthread_handle);
47 void ThreadExit(void *ret);
48 void ThreadJoin(native_handle_type pthread_handle, void **ret);
49 void ThreadSendSignal(native_handle_type pthread_handle, int sig);
50 void ThreadYield();
51
52 WEAK_FOR_LTO_END
53
54 // Templated functions need to be defined here to be accessible everywhere
55
56 namespace internal {
57
58 template <typename T>
59 struct SharedPtrStruct;
60
61 template <typename T>
62 using SharedPtrToSharedPtrStruct = std::shared_ptr<SharedPtrStruct<T>>;
63
64 template <typename T>
65 struct SharedPtrStruct {
66 SharedPtrToSharedPtrStruct<T> this_ptr; // NOLINT(misc-non-private-member-variables-in-classes)
67 T data; // NOLINT(misc-non-private-member-variables-in-classes)
SharedPtrStructpanda::os::thread::internal::SharedPtrStruct68 SharedPtrStruct(SharedPtrToSharedPtrStruct<T> ptr_in, T data_in)
69 : this_ptr(std::move(ptr_in)), data(std::move(data_in))
70 {
71 }
72 };
73
74 template <size_t... Is>
75 struct Seq {
76 };
77
78 template <size_t N, size_t... Is>
79 struct GenArgSeq : GenArgSeq<N - 1, N - 1, Is...> {
80 };
81
82 template <size_t... Is>
83 struct GenArgSeq<1, Is...> : Seq<Is...> {
84 };
85
86 template <class Func, typename Tuple, size_t... I>
CallFunc(Func &func, Tuple &args, Seq<I...> )87 static void CallFunc(Func &func, Tuple &args, Seq<I...> /* unused */)
88 {
89 func(std::get<I>(args)...);
90 }
91
92 template <class Func, typename Tuple, size_t N>
CallFunc(Func &func, Tuple &args)93 static void CallFunc(Func &func, Tuple &args)
94 {
95 CallFunc(func, args, GenArgSeq<N>());
96 }
97
98 template <typename Func, typename Tuple, size_t N>
ProxyFunc(void *args)99 static void *ProxyFunc(void *args)
100 {
101 // Parse pointer and move args to local tuple.
102 // We need this pointer to be destroyed by the time function starts to avoid memleak on thread termination
103 Tuple args_tuple;
104 {
105 auto args_ptr = static_cast<SharedPtrStruct<Tuple> *>(args);
106 SharedPtrToSharedPtrStruct<Tuple> local;
107 // This breaks shared pointer loop
108 local.swap(args_ptr->this_ptr);
109 // This moves tuple data to local variable
110 args_tuple = args_ptr->data;
111 }
112 Func *func = std::get<0>(args_tuple);
113 CallFunc<Func, Tuple, N>(*func, args_tuple);
114 return nullptr;
115 }
116
117 } // namespace internal
118
119 template <typename Func, typename... Args>
ThreadStart(Func *func, Args... args)120 native_handle_type ThreadStart(Func *func, Args... args)
121 {
122 #ifdef PANDA_TARGET_UNIX
123 native_handle_type tid;
124 #else
125 pthread_t tid;
126 #endif
127 auto args_tuple = std::make_tuple(func, std::move(args)...);
128 internal::SharedPtrStruct<decltype(args_tuple)> *ptr = nullptr;
129 {
130 auto shared_ptr = std::make_shared<internal::SharedPtrStruct<decltype(args_tuple)>>(nullptr, args_tuple);
131 ptr = shared_ptr.get();
132 // Make recursive ref to prevent from shared pointer being destroyed until child thread acquires it.
133 ptr->this_ptr = shared_ptr;
134 // Leave scope to make sure that local shared_ptr was destroyed before thread creation
135 }
136 pthread_attr_t attr;
137 pthread_attr_init(&attr);
138 #ifdef PANDA_TARGET_MACOS
139 // In MacOS, the stack size of child thread is 512KB by default. Adjust it to 8MB to be consistent with Linux.
140 size_t stack_size = 8 * 1024 * 1024;
141 pthread_attr_setstacksize(&attr, stack_size);
142 #endif
143 pthread_create(&tid, &attr,
144 &internal::ProxyFunc<Func, decltype(args_tuple), std::tuple_size<decltype(args_tuple)>::value>,
145 static_cast<void *>(ptr));
146 #ifdef PANDA_TARGET_UNIX
147 return tid;
148 #else
149 return reinterpret_cast<native_handle_type>(tid);
150 #endif
151 }
152
153 WEAK_FOR_LTO_START
154 int ThreadGetStackInfo(native_handle_type thread, void **stack_addr, size_t *stack_size, size_t *guard_size);
155 WEAK_FOR_LTO_END
156
IsSetPriorityError(int res)157 inline bool IsSetPriorityError(int res)
158 {
159 #ifdef PANDA_TARGET_UNIX
160 return res != 0;
161 #elif defined(PANDA_TARGET_WINDOWS)
162 return res == 0;
163 #endif
164 }
165 } // namespace panda::os::thread
166
167 #endif // LIBPANDABASE_OS_THREAD_H
168