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) SharedPtrStructSharedPtrStruct68 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> 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> 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> 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> 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_create(&tid, nullptr, 137 &internal::ProxyFunc<Func, decltype(args_tuple), std::tuple_size<decltype(args_tuple)>::value>, 138 static_cast<void *>(ptr)); 139 #ifdef PANDA_TARGET_UNIX 140 return tid; 141 #else 142 return reinterpret_cast<native_handle_type>(tid); 143 #endif 144 } 145 146 WEAK_FOR_LTO_START 147 int ThreadGetStackInfo(native_handle_type thread, void **stack_addr, size_t *stack_size, size_t *guard_size); 148 WEAK_FOR_LTO_END 149 150 inline bool IsSetPriorityError(int res) 151 { 152 #ifdef PANDA_TARGET_UNIX 153 return res != 0; 154 #elif defined(PANDA_TARGET_WINDOWS) 155 return res == 0; 156 #endif 157 } 158 } // namespace panda::os::thread 159 160 #endif // LIBPANDABASE_OS_THREAD_H 161