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 34namespace panda::os::thread { 35 36using ThreadId = uint32_t; 37using native_handle_type = std::thread::native_handle_type; 38 39WEAK_FOR_LTO_START 40 41ThreadId GetCurrentThreadId(); 42int GetPid(); 43int SetThreadName(native_handle_type pthread_handle, const char *name); 44native_handle_type GetNativeHandle(); 45void NativeSleep(unsigned int ms); 46void ThreadDetach(native_handle_type pthread_handle); 47void ThreadExit(void *ret); 48void ThreadJoin(native_handle_type pthread_handle, void **ret); 49void ThreadSendSignal(native_handle_type pthread_handle, int sig); 50void ThreadYield(); 51 52WEAK_FOR_LTO_END 53 54// Templated functions need to be defined here to be accessible everywhere 55 56namespace internal { 57 58template <typename T> 59struct SharedPtrStruct; 60 61template <typename T> 62using SharedPtrToSharedPtrStruct = std::shared_ptr<SharedPtrStruct<T>>; 63 64template <typename T> 65struct 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) 68 SharedPtrStruct(SharedPtrToSharedPtrStruct<T> ptr_in, T data_in) 69 : this_ptr(std::move(ptr_in)), data(std::move(data_in)) 70 { 71 } 72}; 73 74template <size_t... Is> 75struct Seq { 76}; 77 78template <size_t N, size_t... Is> 79struct GenArgSeq : GenArgSeq<N - 1, N - 1, Is...> { 80}; 81 82template <size_t... Is> 83struct GenArgSeq<1, Is...> : Seq<Is...> { 84}; 85 86template <class Func, typename Tuple, size_t... I> 87static void CallFunc(Func &func, Tuple &args, Seq<I...> /* unused */) 88{ 89 func(std::get<I>(args)...); 90} 91 92template <class Func, typename Tuple, size_t N> 93static void CallFunc(Func &func, Tuple &args) 94{ 95 CallFunc(func, args, GenArgSeq<N>()); 96} 97 98template <typename Func, typename Tuple, size_t N> 99static 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 119template <typename Func, typename... Args> 120native_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 153WEAK_FOR_LTO_START 154int ThreadGetStackInfo(native_handle_type thread, void **stack_addr, size_t *stack_size, size_t *guard_size); 155WEAK_FOR_LTO_END 156 157inline 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