Index: clang-tools-extra/clangd/support/Threading.cpp =================================================================== --- clang-tools-extra/clangd/support/Threading.cpp +++ clang-tools-extra/clangd/support/Threading.cpp @@ -2,6 +2,7 @@ #include "support/Trace.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/thread.h" #include "llvm/Support/Threading.h" #include #include @@ -95,8 +96,10 @@ }; // Ensure our worker threads have big enough stacks to run clang. - llvm::llvm_execute_on_thread_async(std::move(Task), - /*clang::DesiredStackSize*/ 8 << 20); + llvm::thread Thread( + /*clang::DesiredStackSize*/ llvm::Optional(8 << 20), + std::move(Task)); + Thread.detach(); } Deadline timeoutSeconds(llvm::Optional Seconds) { Index: clang/tools/libclang/CIndex.cpp =================================================================== --- clang/tools/libclang/CIndex.cpp +++ clang/tools/libclang/CIndex.cpp @@ -52,6 +52,7 @@ #include "llvm/Support/SaveAndRestore.h" #include "llvm/Support/Signals.h" #include "llvm/Support/TargetSelect.h" +#include "llvm/Support/thread.h" #include "llvm/Support/Threading.h" #include "llvm/Support/Timer.h" #include "llvm/Support/raw_ostream.h" @@ -6770,10 +6771,10 @@ void clang_executeOnThread(void (*fn)(void *), void *user_data, unsigned stack_size) { - llvm::llvm_execute_on_thread(fn, user_data, - stack_size == 0 - ? clang::DesiredStackSize - : llvm::Optional(stack_size)); + llvm::thread Thread(stack_size == 0 ? clang::DesiredStackSize + : llvm::Optional(stack_size), + fn, user_data); + Thread.join(); } //===----------------------------------------------------------------------===// Index: llvm/include/llvm/Support/CrashRecoveryContext.h =================================================================== --- llvm/include/llvm/Support/CrashRecoveryContext.h +++ llvm/include/llvm/Support/CrashRecoveryContext.h @@ -87,7 +87,7 @@ /// 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. Index: llvm/include/llvm/Support/Threading.h =================================================================== --- llvm/include/llvm/Support/Threading.h +++ llvm/include/llvm/Support/Threading.h @@ -54,36 +54,6 @@ /// 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; Index: llvm/include/llvm/Support/thread.h =================================================================== --- llvm/include/llvm/Support/thread.h +++ llvm/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 +#include "llvm/Support/Windows/WindowsSupport.h" +#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 = HANDLE; + 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(); + } + + id get_id() const noexcept { + return Thread; + } + + 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); + +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(); +} + +void thread::join() { + llvm_thread_join_impl(Thread); + Thread = native_handle_type(); +} + +void thread::detach() { + llvm_thread_detach_impl(Thread); + Thread = native_handle_type(); } +#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; +}; + +#endif // LLVM_ON_UNIX || _WIN32 + +} // namespace llvm + #else // !LLVM_ENABLE_THREADS #include @@ -36,11 +235,20 @@ thread() {} thread(thread &&other) {} template + 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; }; }; @@ -49,4 +257,4 @@ #endif // LLVM_ENABLE_THREADS -#endif +#endif // LLVM_SUPPORT_THREAD_H \ No newline at end of file Index: llvm/lib/Support/CrashRecoveryContext.cpp =================================================================== --- llvm/lib/Support/CrashRecoveryContext.cpp +++ llvm/lib/Support/CrashRecoveryContext.cpp @@ -12,6 +12,7 @@ #include "llvm/Support/ExitCodes.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/Signals.h" +#include "llvm/Support/thread.h" #include "llvm/Support/ThreadLocal.h" #include #include @@ -500,10 +501,12 @@ 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; Index: llvm/lib/Support/Threading.cpp =================================================================== --- llvm/lib/Support/Threading.cpp +++ llvm/lib/Support/Threading.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "llvm/Support/thread.h" #include "llvm/Support/Threading.h" #include "llvm/ADT/Optional.h" #include "llvm/Config/config.h" @@ -38,13 +39,6 @@ #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 @@ 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 @@ 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 @@ #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 Index: llvm/lib/Support/Unix/Threading.inc =================================================================== --- llvm/lib/Support/Unix/Threading.inc +++ llvm/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,15 +77,22 @@ 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); } } Index: llvm/lib/Support/Windows/Threading.inc =================================================================== --- llvm/lib/Support/Windows/Threading.inc +++ llvm/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,11 +34,16 @@ 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"); } Index: llvm/unittests/Support/Threading.cpp =================================================================== --- llvm/unittests/Support/Threading.cpp +++ llvm/unittests/Support/Threading.cpp @@ -63,7 +63,8 @@ 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, 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