diff --git a/include/llvm/Support/CrashRecoveryContext.h b/include/llvm/Support/CrashRecoveryContext.h index f756635ee1f..498690655fd 100644 --- a/include/llvm/Support/CrashRecoveryContext.h +++ b/include/llvm/Support/CrashRecoveryContext.h @@ -87,7 +87,7 @@ public: /// a protected context which is run in another thread (optionally with a /// requested stack size). /// - /// See RunSafely() and llvm_execute_on_thread(). + /// See RunSafely(). /// /// On Darwin, if PRIO_DARWIN_BG is set on the calling thread, it will be /// propagated to the new thread as well. diff --git a/include/llvm/Support/Threading.h b/include/llvm/Support/Threading.h index e489caf47e9..94de950d447 100644 --- a/include/llvm/Support/Threading.h +++ b/include/llvm/Support/Threading.h @@ -55,36 +55,6 @@ class Twine; /// false otherwise. bool llvm_is_multithreaded(); -/// Execute the given \p UserFn on a separate thread, passing it the provided \p -/// UserData and waits for thread completion. -/// -/// This function does not guarantee that the code will actually be executed -/// on a separate thread or honoring the requested stack size, but tries to do -/// so where system support is available. -/// -/// \param UserFn - The callback to execute. -/// \param UserData - An argument to pass to the callback function. -/// \param StackSizeInBytes - A requested size (in bytes) for the thread stack -/// (or None for default) -void llvm_execute_on_thread( - void (*UserFn)(void *), void *UserData, - llvm::Optional StackSizeInBytes = llvm::None); - -/// Schedule the given \p Func for execution on a separate thread, then return -/// to the caller immediately. Roughly equivalent to -/// `std::thread(Func).detach()`, except it allows requesting a specific stack -/// size, if supported for the platform. -/// -/// This function would report a fatal error if it can't execute the code -/// on a separate thread. -/// -/// \param Func - The callback to execute. -/// \param StackSizeInBytes - A requested size (in bytes) for the thread stack -/// (or None for default) -void llvm_execute_on_thread_async( - llvm::unique_function Func, - llvm::Optional StackSizeInBytes = llvm::None); - #if LLVM_THREADING_USE_STD_CALL_ONCE typedef std::once_flag once_flag; diff --git a/include/llvm/Support/thread.h b/include/llvm/Support/thread.h index 084ed16166f..93b8370a3ad 100644 --- a/include/llvm/Support/thread.h +++ b/include/llvm/Support/thread.h @@ -16,16 +16,215 @@ #ifndef LLVM_SUPPORT_THREAD_H #define LLVM_SUPPORT_THREAD_H +#include "llvm/ADT/Optional.h" #include "llvm/Config/llvm-config.h" +#ifdef _WIN32 +typedef unsigned long DWORD; +typedef void *PVOID; +typedef PVOID HANDLE; +#endif + #if LLVM_ENABLE_THREADS #include namespace llvm { -typedef std::thread thread; + +#if LLVM_ON_UNIX || _WIN32 + +/// LLVM thread following std::thread interface with added constructor to +/// specify stack size. +class thread { + template + static void Apply(std::tuple &Callee, + std::index_sequence) { + std::move(std::get<0>(Callee))(std::move(std::get(Callee))...); + } + + template static void GenericThreadProxy(void *Ptr) { + std::unique_ptr Callee(static_cast(Ptr)); + + // FIXME: use std::apply when C++17 is allowed. + std::make_index_sequence() - 1> Indices{}; + Apply(*Callee.get(), Indices); + } + +public: +#if LLVM_ON_UNIX + using native_handle_type = pthread_t; + using id = pthread_t; + using start_routine_type = void *(*)(void *); + + template static void *ThreadProxy(void *Ptr) { + GenericThreadProxy(Ptr); + return nullptr; + } +#elif _WIN32 + using native_handle_type = HANDLE; + using id = DWORD; + using start_routine_type = unsigned(__stdcall *)(void *); + + template + static unsigned __stdcall ThreadProxy(void *Ptr) { + GenericThreadProxy(Ptr); + return 0; + } +#endif + +#if defined(__APPLE__) + // Darwin's default stack size for threads except the main one is only 512KB, + // which is not enough for some/many normal LLVM compilations. This implements + // the same interface as std::thread but requests the same stack size as the + // main thread (8MB) before creation. + static const constexpr llvm::Optional DefaultStackSize = + 8 * 1024 * 1024; +#else + static const constexpr llvm::Optional DefaultStackSize = None; +#endif + + thread() : Thread(native_handle_type()) {} + thread(thread &&Other) noexcept + : Thread(std::exchange(Other.Thread, native_handle_type())) {} + + template + explicit thread(Function &&f, Args &&...args) + : thread(DefaultStackSize, f, args...) {} + + template + explicit thread(llvm::Optional StackSizeInBytes, Function &&f, + Args &&...args); + thread(const thread &) = delete; + + ~thread() { + if (joinable()) + std::terminate(); + } + + thread &operator=(thread &&Other) noexcept { + if (joinable()) + std::terminate(); + Thread = std::exchange(Other.Thread, native_handle_type()); + return *this; + } + + bool joinable() const noexcept { return Thread != native_handle_type(); } + + inline id get_id() const noexcept; + + native_handle_type native_handle() const noexcept { return Thread; } + + static unsigned hardware_concurrency() { + return std::thread::hardware_concurrency(); + }; + + inline void join(); + inline void detach(); + + void swap(llvm::thread &Other) noexcept { std::swap(Thread, Other.Thread); } + +private: + native_handle_type Thread; +}; + +thread::native_handle_type +llvm_execute_on_thread_impl(thread::start_routine_type ThreadFunc, void *Arg, + llvm::Optional StackSizeInBytes); +void llvm_thread_join_impl(thread::native_handle_type Thread); +void llvm_thread_detach_impl(thread::native_handle_type Thread); +thread::id llvm_thread_get_id_impl(thread::native_handle_type Thread); +thread::id llvm_thread_get_current_id_impl(); + +template +thread::thread(llvm::Optional StackSizeInBytes, Function &&f, + Args &&...args) { + typedef std::tuple::type, + typename std::decay::type...> + CalleeTuple; + std::unique_ptr Callee( + new CalleeTuple(std::forward(f), std::forward(args)...)); + + Thread = llvm_execute_on_thread_impl(ThreadProxy, Callee.get(), + StackSizeInBytes); + if (Thread != native_handle_type()) + Callee.release(); } +thread::id thread::get_id() const noexcept { + return llvm_thread_get_id_impl(Thread); +} + +void thread::join() { + llvm_thread_join_impl(Thread); + Thread = native_handle_type(); +} + +void thread::detach() { + llvm_thread_detach_impl(Thread); + Thread = native_handle_type(); +} + +namespace this_thread { +inline thread::id get_id() { return llvm_thread_get_current_id_impl(); } +} // namespace this_thread + +#else // !LLVM_ON_UNIX && !_WIN32 + +/// std::thread backed implementation of llvm::thread interface that ignores the +/// stack size request. +class thread { +public: + using native_handle_type = std::thread::native_handle_type; + using id = std::thread::id; + + thread() : Thread(std::thread()) {} + thread(thread &&Other) noexcept + : Thread(std::exchange(Other.Thread, std::thread())) {} + + template + explicit thread(llvm::Optional StackSizeInBytes, Function &&f, + Args &&...args) + : Thread(std::forward(f), std::forward(args)...) {} + + template + explicit thread(Function &&f, Args &&...args) : Thread(f, args...) {} + + thread(const thread &) = delete; + + ~thread() {} + + thread &operator=(thread &&Other) noexcept { + Thread = std::exchange(Other.Thread, std::thread()); + return *this; + } + + bool joinable() const noexcept { return Thread.joinable(); } + + id get_id() const noexcept { return Thread.get_id(); } + + native_handle_type native_handle() noexcept { return Thread.native_handle(); } + + static unsigned hardware_concurrency() { + return std::thread::hardware_concurrency(); + }; + + inline void join() { Thread.join(); } + inline void detach() { Thread.detach(); } + + void swap(llvm::thread &Other) noexcept { std::swap(Thread, Other.Thread); } + +private: + std::thread Thread; +}; + +namespace this_thread { + inline thread::id get_id() { return std::this_thread::get_id(); } +} + +#endif // LLVM_ON_UNIX || _WIN32 + +} // namespace llvm + #else // !LLVM_ENABLE_THREADS #include @@ -36,17 +235,26 @@ struct thread { thread() {} thread(thread &&other) {} template - explicit thread(Function &&f, Args &&... args) { + explicit thread(llvm::Optional StackSizeInBytes, Function &&f, + Args &&...args) { + f(std::forward(args)...); + } + template + explicit thread(Function &&f, Args &&...args) { f(std::forward(args)...); } thread(const thread &) = delete; + void detach() { + report_fatal_error("Detaching from a thread does not make sense with no " + "threading support"); + } void join() {} static unsigned hardware_concurrency() { return 1; }; }; -} +} // namespace llvm #endif // LLVM_ENABLE_THREADS -#endif +#endif // LLVM_SUPPORT_THREAD_H diff --git a/lib/Support/CrashRecoveryContext.cpp b/lib/Support/CrashRecoveryContext.cpp index 3ee6a4fd36f..433d99df593 100644 --- a/lib/Support/CrashRecoveryContext.cpp +++ b/lib/Support/CrashRecoveryContext.cpp @@ -13,6 +13,7 @@ #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/Signals.h" #include "llvm/Support/ThreadLocal.h" +#include "llvm/Support/thread.h" #include #include @@ -500,10 +501,12 @@ bool CrashRecoveryContext::RunSafelyOnThread(function_ref Fn, unsigned RequestedStackSize) { bool UseBackgroundPriority = hasThreadBackgroundPriority(); RunSafelyOnThreadInfo Info = { Fn, this, UseBackgroundPriority, false }; - llvm_execute_on_thread(RunSafelyOnThread_Dispatch, &Info, - RequestedStackSize == 0 - ? llvm::None - : llvm::Optional(RequestedStackSize)); + llvm::thread Thread(RequestedStackSize == 0 + ? llvm::None + : llvm::Optional(RequestedStackSize), + RunSafelyOnThread_Dispatch, &Info); + Thread.join(); + if (CrashRecoveryContextImpl *CRC = (CrashRecoveryContextImpl *)Impl) CRC->setSwitchedThread(); return Info.Result; diff --git a/lib/Support/ThreadPool.cpp b/lib/Support/ThreadPool.cpp index f442b3b0bc9..81926d8071b 100644 --- a/lib/Support/ThreadPool.cpp +++ b/lib/Support/ThreadPool.cpp @@ -73,8 +73,8 @@ void ThreadPool::wait() { } bool ThreadPool::isWorkerThread() const { - std::thread::id CurrentThreadId = std::this_thread::get_id(); - for (const std::thread &Thread : Threads) + llvm::thread::id CurrentThreadId = llvm::this_thread::get_id(); + for (const llvm::thread &Thread : Threads) if (CurrentThreadId == Thread.get_id()) return true; return false; diff --git a/lib/Support/Threading.cpp b/lib/Support/Threading.cpp index 61f8ee5be5b..55e97d60af7 100644 --- a/lib/Support/Threading.cpp +++ b/lib/Support/Threading.cpp @@ -15,6 +15,7 @@ #include "llvm/ADT/Optional.h" #include "llvm/Config/config.h" #include "llvm/Support/Host.h" +#include "llvm/Support/thread.h" #include #include @@ -38,13 +39,6 @@ bool llvm::llvm_is_multithreaded() { #if LLVM_ENABLE_THREADS == 0 || \ (!defined(_WIN32) && !defined(HAVE_PTHREAD_H)) -// Support for non-Win32, non-pthread implementation. -void llvm::llvm_execute_on_thread(void (*Fn)(void *), void *UserData, - llvm::Optional StackSizeInBytes) { - (void)StackSizeInBytes; - Fn(UserData); -} - uint64_t llvm::get_threadid() { return 0; } uint32_t llvm::get_max_thread_name_length() { return 0; } @@ -60,25 +54,6 @@ unsigned llvm::ThreadPoolStrategy::compute_thread_count() const { return 1; } -#if LLVM_ENABLE_THREADS == 0 -void llvm::llvm_execute_on_thread_async( - llvm::unique_function Func, - llvm::Optional StackSizeInBytes) { - (void)Func; - (void)StackSizeInBytes; - report_fatal_error("Spawning a detached thread doesn't make sense with no " - "threading support"); -} -#else -// Support for non-Win32, non-pthread implementation. -void llvm::llvm_execute_on_thread_async( - llvm::unique_function Func, - llvm::Optional StackSizeInBytes) { - (void)StackSizeInBytes; - std::thread(std::move(Func)).detach(); -} -#endif - #else int computeHostNumHardwareThreads(); @@ -95,17 +70,6 @@ unsigned llvm::ThreadPoolStrategy::compute_thread_count() const { return std::min((unsigned)MaxThreadCount, ThreadsRequested); } -namespace { -struct SyncThreadInfo { - void (*UserFn)(void *); - void *UserData; -}; - -using AsyncThreadInfo = llvm::unique_function; - -enum class JoiningPolicy { Join, Detach }; -} // namespace - // Include the platform-specific parts of this class. #ifdef LLVM_ON_UNIX #include "Unix/Threading.inc" @@ -114,22 +78,6 @@ enum class JoiningPolicy { Join, Detach }; #include "Windows/Threading.inc" #endif -void llvm::llvm_execute_on_thread(void (*Fn)(void *), void *UserData, - llvm::Optional StackSizeInBytes) { - - SyncThreadInfo Info = {Fn, UserData}; - llvm_execute_on_thread_impl(threadFuncSync, &Info, StackSizeInBytes, - JoiningPolicy::Join); -} - -void llvm::llvm_execute_on_thread_async( - llvm::unique_function Func, - llvm::Optional StackSizeInBytes) { - llvm_execute_on_thread_impl(&threadFuncAsync, - new AsyncThreadInfo(std::move(Func)), - StackSizeInBytes, JoiningPolicy::Detach); -} - #endif Optional diff --git a/lib/Support/Unix/Threading.inc b/lib/Support/Unix/Threading.inc index 667d023fc13..2131defadbb 100644 --- a/lib/Support/Unix/Threading.inc +++ b/lib/Support/Unix/Threading.inc @@ -48,22 +48,9 @@ #include // For syscall() #endif -static void *threadFuncSync(void *Arg) { - SyncThreadInfo *TI = static_cast(Arg); - TI->UserFn(TI->UserData); - return nullptr; -} - -static void *threadFuncAsync(void *Arg) { - std::unique_ptr Info(static_cast(Arg)); - (*Info)(); - return nullptr; -} - -static void -llvm_execute_on_thread_impl(void *(*ThreadFunc)(void *), void *Arg, - llvm::Optional StackSizeInBytes, - JoiningPolicy JP) { +pthread_t +llvm::llvm_execute_on_thread_impl(void *(*ThreadFunc)(void *), void *Arg, + llvm::Optional StackSizeInBytes) { int errnum; // Construct the attributes object. @@ -90,18 +77,33 @@ llvm_execute_on_thread_impl(void *(*ThreadFunc)(void *), void *Arg, if ((errnum = ::pthread_create(&Thread, &Attr, ThreadFunc, Arg)) != 0) ReportErrnumFatal("pthread_create failed", errnum); - if (JP == JoiningPolicy::Join) { - // Wait for the thread - if ((errnum = ::pthread_join(Thread, nullptr)) != 0) { - ReportErrnumFatal("pthread_join failed", errnum); - } - } else if (JP == JoiningPolicy::Detach) { - if ((errnum = ::pthread_detach(Thread)) != 0) { - ReportErrnumFatal("pthread_detach failed", errnum); - } + return Thread; +} + +void llvm::llvm_thread_detach_impl(pthread_t Thread) { + int errnum; + + if ((errnum = ::pthread_detach(Thread)) != 0) { + ReportErrnumFatal("pthread_detach failed", errnum); } } +void llvm::llvm_thread_join_impl(pthread_t Thread) { + int errnum; + + if ((errnum = ::pthread_join(Thread, nullptr)) != 0) { + ReportErrnumFatal("pthread_join failed", errnum); + } +} + +pthread_t llvm::llvm_thread_get_id_impl(pthread_t Thread) { + return Thread; +} + +pthread_t llvm::llvm_thread_get_current_id_impl() { + return ::pthread_self(); +} + uint64_t llvm::get_threadid() { #if defined(__APPLE__) // Calling "mach_thread_self()" bumps the reference count on the thread diff --git a/lib/Support/Windows/Threading.inc b/lib/Support/Windows/Threading.inc index 6448bb478d0..12d8cbc21cb 100644 --- a/lib/Support/Windows/Threading.inc +++ b/lib/Support/Windows/Threading.inc @@ -23,22 +23,10 @@ #undef MemoryFence #endif -static unsigned __stdcall threadFuncSync(void *Arg) { - SyncThreadInfo *TI = static_cast(Arg); - TI->UserFn(TI->UserData); - return 0; -} - -static unsigned __stdcall threadFuncAsync(void *Arg) { - std::unique_ptr Info(static_cast(Arg)); - (*Info)(); - return 0; -} - -static void -llvm_execute_on_thread_impl(unsigned (__stdcall *ThreadFunc)(void *), void *Arg, - llvm::Optional StackSizeInBytes, - JoiningPolicy JP) { +HANDLE +llvm::llvm_execute_on_thread_impl(unsigned(__stdcall *ThreadFunc)(void *), + void *Arg, + llvm::Optional StackSizeInBytes) { HANDLE hThread = (HANDLE)::_beginthreadex( NULL, StackSizeInBytes.getValueOr(0), ThreadFunc, Arg, 0, NULL); @@ -46,16 +34,29 @@ llvm_execute_on_thread_impl(unsigned (__stdcall *ThreadFunc)(void *), void *Arg, ReportLastErrorFatal("_beginthreadex failed"); } - if (JP == JoiningPolicy::Join) { - if (::WaitForSingleObject(hThread, INFINITE) == WAIT_FAILED) { - ReportLastErrorFatal("WaitForSingleObject failed"); - } + return hThread; +} + +void llvm::llvm_thread_join_impl(HANDLE hThread) { + if (::WaitForSingleObject(hThread, INFINITE) == WAIT_FAILED) { + ReportLastErrorFatal("WaitForSingleObject failed"); } +} + +void llvm::llvm_thread_detach_impl(HANDLE hThread) { if (::CloseHandle(hThread) == FALSE) { ReportLastErrorFatal("CloseHandle failed"); } } +DWORD llvm::llvm_thread_get_id_impl(HANDLE hThread) { + return ::GetThreadId(hThread); +} + +DWORD llvm::llvm_thread_get_current_id_impl() { + return ::GetCurrentThreadId(); +} + uint64_t llvm::get_threadid() { return uint64_t(::GetCurrentThreadId()); } diff --git a/unittests/Support/Threading.cpp b/unittests/Support/Threading.cpp index c76e6e4a5bd..69a29872b18 100644 --- a/unittests/Support/Threading.cpp +++ b/unittests/Support/Threading.cpp @@ -63,7 +63,8 @@ TEST(Threading, RunOnThreadSyncAsync) { ThreadFinished.notify(); }; - llvm::llvm_execute_on_thread_async(ThreadFunc); + llvm::thread Thread(ThreadFunc); + Thread.detach(); ASSERT_TRUE(ThreadStarted.wait()); ThreadAdvanced.notify(); ASSERT_TRUE(ThreadFinished.wait()); @@ -71,11 +72,23 @@ TEST(Threading, RunOnThreadSyncAsync) { TEST(Threading, RunOnThreadSync) { std::atomic_bool Executed(false); - llvm::llvm_execute_on_thread( + llvm::thread Thread( [](void *Arg) { *static_cast(Arg) = true; }, &Executed); + Thread.join(); ASSERT_EQ(Executed, true); } + +#if defined(__APPLE__) +TEST(Threading, AppleStackSize) { + llvm::thread Thread([] { + volatile unsigned char Var[8 * 1024 * 1024 - 1024]; + Var[0] = 0xff; + ASSERT_EQ(Var[0], 0xff); + }); + Thread.join(); +} +#endif #endif } // end anon namespace