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