Index: include/llvm/Support/Threading.h =================================================================== --- include/llvm/Support/Threading.h +++ include/llvm/Support/Threading.h @@ -15,6 +15,7 @@ #ifndef LLVM_SUPPORT_THREADING_H #define LLVM_SUPPORT_THREADING_H +#include "llvm/ADT/FunctionExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Config/llvm-config.h" // for LLVM_ON_UNIX #include "llvm/Support/Compiler.h" @@ -59,10 +60,27 @@ /// /// \param UserFn - The callback to execute. /// \param UserData - An argument to pass to the callback function. -/// \param RequestedStackSize - If non-zero, a requested size (in bytes) for -/// the thread stack. -void llvm_execute_on_thread(void (*UserFn)(void *), void *UserData, - unsigned RequestedStackSize = 0); +/// \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. +/// +/// 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 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 Index: lib/Support/Threading.cpp =================================================================== --- lib/Support/Threading.cpp +++ lib/Support/Threading.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "llvm/Support/Threading.h" +#include "llvm/ADT/Optional.h" #include "llvm/Config/config.h" #include "llvm/Support/Host.h" @@ -85,6 +86,17 @@ return 1; } +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" @@ -93,4 +105,21 @@ #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) { + std::unique_ptr Info(new AsyncThreadInfo(std::move(Func))); + llvm_execute_on_thread_impl(&threadFuncAsync, Info.get(), StackSizeInBytes, + JoiningPolicy::Detach); + Info.release(); +} + #endif Index: lib/Support/Unix/Threading.inc =================================================================== --- lib/Support/Unix/Threading.inc +++ lib/Support/Unix/Threading.inc @@ -11,6 +11,8 @@ // //===----------------------------------------------------------------------===// +#include "Unix.h" +#include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/Twine.h" @@ -41,47 +43,54 @@ #include // For syscall() #endif -namespace { - struct ThreadInfo { - void(*UserFn)(void *); - void *UserData; - }; +static void *threadFuncSync(void *Arg) { + SyncThreadInfo *TI = static_cast(Arg); + TI->UserFn(TI->UserData); + return nullptr; } -static void *ExecuteOnThread_Dispatch(void *Arg) { - ThreadInfo *TI = reinterpret_cast(Arg); - TI->UserFn(TI->UserData); +static void *threadFuncAsync(void *Arg) { + std::unique_ptr Info(static_cast(Arg)); + (*Info)(); return nullptr; } -void llvm::llvm_execute_on_thread(void(*Fn)(void*), void *UserData, - unsigned RequestedStackSize) { - ThreadInfo Info = { Fn, UserData }; +static void +llvm_execute_on_thread_impl(void *(*ThreadFunc)(void *), void *Arg, + llvm::Optional StackSizeInBytes, + JoiningPolicy JP) { + // Construct the attributes object. pthread_attr_t Attr; - pthread_t Thread; + if (::pthread_attr_init(&Attr) != 0) { + ReportErrnoFatal("pthread_attr_init failed"); + } - // Construct the attributes object. - if (::pthread_attr_init(&Attr) != 0) - return; + auto AttrGuard = llvm::make_scope_exit([&] { + if (::pthread_attr_destroy(&Attr) != 0) { + ReportErrnoFatal("pthread_attr_destroy failed"); + } + }); // Set the requested stack size, if given. - if (RequestedStackSize != 0) { - if (::pthread_attr_setstacksize(&Attr, RequestedStackSize) != 0) - goto error; + if (StackSizeInBytes) { + if (::pthread_attr_setstacksize(&Attr, *StackSizeInBytes) != 0) { + ReportErrnoFatal("pthread_attr_setstacksize failed"); + } } // Construct and execute the thread. - if (::pthread_create(&Thread, &Attr, ExecuteOnThread_Dispatch, &Info) != 0) - goto error; - - // Wait for the thread and clean up. - ::pthread_join(Thread, nullptr); + pthread_t Thread; + if (::pthread_create(&Thread, &Attr, ThreadFunc, Arg) != 0) + ReportErrnoFatal("pthread_create failed"); -error: - ::pthread_attr_destroy(&Attr); + if (JP == JoiningPolicy::Join) { + // Wait for the thread + if (::pthread_join(Thread, nullptr) != 0) { + ReportErrnoFatal("pthread_join failed"); + } + } } - uint64_t llvm::get_threadid() { #if defined(__APPLE__) // Calling "mach_thread_self()" bumps the reference count on the thread Index: lib/Support/Unix/Unix.h =================================================================== --- lib/Support/Unix/Unix.h +++ lib/Support/Unix/Unix.h @@ -22,6 +22,7 @@ #include "llvm/Config/config.h" // Get autoconf configuration settings #include "llvm/Support/Chrono.h" #include "llvm/Support/Errno.h" +#include "llvm/Support/ErrorHandling.h" #include #include #include @@ -70,6 +71,13 @@ return true; } +// Include StrError() in a fatal error message. +LLVM_ATTRIBUTE_NORETURN static inline void ReportErrnoFatal(const char *Msg) { + std::string ErrMsg; + MakeErrMsg(&ErrMsg, Msg); + llvm::report_fatal_error(ErrMsg); +} + namespace llvm { namespace sys { Index: lib/Support/Windows/Process.inc =================================================================== --- lib/Support/Windows/Process.inc +++ lib/Support/Windows/Process.inc @@ -441,13 +441,6 @@ return 0; } -// Include GetLastError() in a fatal error message. -static void ReportLastErrorFatal(const char *Msg) { - std::string ErrMsg; - MakeErrMsg(&ErrMsg, Msg); - report_fatal_error(ErrMsg); -} - unsigned Process::GetRandomNumber() { HCRYPTPROV HCPC; if (!::CryptAcquireContextW(&HCPC, NULL, NULL, PROV_RSA_FULL, Index: lib/Support/Windows/Threading.inc =================================================================== --- lib/Support/Windows/Threading.inc +++ lib/Support/Windows/Threading.inc @@ -22,36 +22,36 @@ #undef MemoryFence #endif -namespace { - struct ThreadInfo { - void(*func)(void*); - void *param; - }; +static unsigned __stdcall threadFuncSync(void *Arg) { + SyncThreadInfo *TI = static_cast(Arg); + TI->UserFn(TI->UserData); + return 0; } -static unsigned __stdcall ThreadCallback(void *param) { - struct ThreadInfo *info = reinterpret_cast(param); - info->func(info->param); - +static unsigned __stdcall threadFuncAsync(void *Arg) { + std::unique_ptr Info(static_cast(Arg)); + (*Info)(); return 0; } -void llvm::llvm_execute_on_thread(void(*Fn)(void*), void *UserData, - unsigned RequestedStackSize) { - struct ThreadInfo param = { Fn, UserData }; - - HANDLE hThread = (HANDLE)::_beginthreadex(NULL, - RequestedStackSize, ThreadCallback, - ¶m, 0, NULL); - - if (hThread) { - // We actually don't care whether the wait succeeds or fails, in - // the same way we don't care whether the pthread_join call succeeds - // or fails. There's not much we could do if this were to fail. But - // on success, this call will wait until the thread finishes executing - // before returning. - (void)::WaitForSingleObject(hThread, INFINITE); - ::CloseHandle(hThread); +static void +llvm_execute_on_thread_impl(_beginthreadex_proc_type ThreadFunc, void *Arg, + llvm::Optional StackSizeInBytes, + JoiningPolicy JP) { + HANDLE hThread = (HANDLE)::_beginthreadex( + NULL, StackSizeInBytes.getValueOr(0), ThreadFunc, Arg, 0, NULL); + + if (!hThread) { + ReportLastErrorFatal("_beginthreadex failed"); + } + + if (JP == JoiningPolicy::Join) { + if (::WaitForSingleObject(hThread, INFINITE) == WAIT_FAILED) { + ReportLastErrorFatal("WaitForSingleObject failed"); + } + } + if (::CloseHandle(hThread) == FALSE) { + ReportLastErrorFatal("CloseHandle failed"); } } Index: lib/Support/Windows/WindowsSupport.h =================================================================== --- lib/Support/Windows/WindowsSupport.h +++ lib/Support/Windows/WindowsSupport.h @@ -41,6 +41,7 @@ #include "llvm/Config/config.h" // Get build system configuration settings #include "llvm/Support/Chrono.h" #include "llvm/Support/Compiler.h" +#include "llvm/Support/ErrorHandling.h" #include #include #include @@ -90,6 +91,13 @@ return R != 0; } +// Include GetLastError() in a fatal error message. +LLVM_ATTRIBUTE_NORETURN inline void ReportLastErrorFatal(const char *Msg) { + std::string ErrMsg; + MakeErrMsg(&ErrMsg, Msg); + llvm::report_fatal_error(ErrMsg); +} + template class ScopedHandle { typedef typename HandleTraits::handle_type handle_type; Index: unittests/Support/Threading.cpp =================================================================== --- unittests/Support/Threading.cpp +++ unittests/Support/Threading.cpp @@ -11,10 +11,37 @@ #include "llvm/Support/thread.h" #include "gtest/gtest.h" +#include + using namespace llvm; namespace { +class Notification { +public: + void notify() { + { + std::lock_guard Lock(M); + Notified = true; + } + CV.notify_all(); + } + + bool wait() { + std::unique_lock Lock(M); + using steady_clock = std::chrono::steady_clock; + auto Deadline = steady_clock::now() + + std::chrono::duration_cast( + std::chrono::duration(5)); + return CV.wait_until(Lock, Deadline, [this] { return Notified; }); + } + +private: + bool Notified = false; + mutable std::condition_variable CV; + mutable std::mutex M; +}; + TEST(Threading, PhysicalConcurrency) { auto Num = heavyweight_hardware_concurrency(); // Since Num is unsigned this will also catch us trying to @@ -22,4 +49,27 @@ ASSERT_LE(Num, thread::hardware_concurrency()); } +TEST(Threading, RunOnThreadSyncAsync) { + Notification ThreadStarted, ThreadAdvanced, ThreadFinished; + + auto ThreadFunc = [&] { + ThreadStarted.notify(); + ASSERT_TRUE(ThreadAdvanced.wait()); + ThreadFinished.notify(); + }; + + llvm::llvm_execute_on_thread_async(ThreadFunc); + ASSERT_TRUE(ThreadStarted.wait()); + ThreadAdvanced.notify(); + ASSERT_TRUE(ThreadFinished.wait()); +} + +TEST(Threading, RunOnThreadSync) { + std::atomic_bool Executed(false); + llvm::llvm_execute_on_thread( + [](void *Arg) { *static_cast(Arg) = true; }, + &Executed); + ASSERT_EQ(Executed, true); +} + } // end anon namespace