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" @@ -64,6 +65,21 @@ void llvm_execute_on_thread(void (*UserFn)(void *), void *UserData, unsigned RequestedStackSize = 0); +/// 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 RequestedStackSize - If non-zero, a requested size (in bytes) for +/// the thread stack. +void llvm_execute_on_thread_async(llvm::unique_function Func, + unsigned RequestedStackSize = 0); + #if LLVM_THREADING_USE_STD_CALL_ONCE typedef std::once_flag once_flag; Index: lib/Support/Threading.cpp =================================================================== --- lib/Support/Threading.cpp +++ lib/Support/Threading.cpp @@ -85,6 +85,15 @@ return 1; } +namespace { +struct SyncThreadInfo { + void (*UserFn)(void *); + void *UserData; +}; + +using AsyncThreadInfo = llvm::unique_function; +} // namespace + // Include the platform-specific parts of this class. #ifdef LLVM_ON_UNIX #include "Unix/Threading.inc" @@ -93,4 +102,20 @@ #include "Windows/Threading.inc" #endif +void llvm::llvm_execute_on_thread(void (*Fn)(void *), void *UserData, + unsigned RequestedStackSize) { + + SyncThreadInfo Info = {Fn, UserData}; + llvm_execute_on_thread_impl(threadFuncSync, &Info, RequestedStackSize, true); +} + +void llvm::llvm_execute_on_thread_async(llvm::unique_function Func, + unsigned RequestedStackSize) { + std::unique_ptr Info(new AsyncThreadInfo(std::move(Func))); + if (llvm_execute_on_thread_impl(&threadFuncAsync, Info.get(), + RequestedStackSize, false)) { + Info.release(); + } +} + #endif Index: lib/Support/Unix/Threading.inc =================================================================== --- lib/Support/Unix/Threading.inc +++ lib/Support/Unix/Threading.inc @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/Twine.h" @@ -41,47 +42,48 @@ #include // For syscall() #endif -namespace { - struct ThreadInfo { - void(*UserFn)(void *); - void *UserData; - }; -} - -static void *ExecuteOnThread_Dispatch(void *Arg) { - ThreadInfo *TI = reinterpret_cast(Arg); +static void *threadFuncSync(void *Arg) { + SyncThreadInfo *TI = static_cast(Arg); TI->UserFn(TI->UserData); return nullptr; } -void llvm::llvm_execute_on_thread(void(*Fn)(void*), void *UserData, - unsigned RequestedStackSize) { - ThreadInfo Info = { Fn, UserData }; - pthread_attr_t Attr; - pthread_t Thread; +static void *threadFuncAsync(void *Arg) { + std::unique_ptr Info(static_cast(Arg)); + (*Info)(); + return nullptr; +} +static bool llvm_execute_on_thread_impl(void *(*ThreadFunc)(void *), void *Arg, + unsigned RequestedStackSize, + bool ShouldJoin) { // Construct the attributes object. + pthread_attr_t Attr; if (::pthread_attr_init(&Attr) != 0) - return; + return false; + + auto AttrGuard = + llvm::make_scope_exit([&] { ::pthread_attr_destroy(&Attr); }); // Set the requested stack size, if given. if (RequestedStackSize != 0) { if (::pthread_attr_setstacksize(&Attr, RequestedStackSize) != 0) - goto error; + return false; } // Construct and execute the thread. - if (::pthread_create(&Thread, &Attr, ExecuteOnThread_Dispatch, &Info) != 0) - goto error; + pthread_t Thread; + if (::pthread_create(&Thread, &Attr, ThreadFunc, Arg) != 0) + return false; - // Wait for the thread and clean up. - ::pthread_join(Thread, nullptr); + if (ShouldJoin) { + // Wait for the thread + ::pthread_join(Thread, nullptr); + } -error: - ::pthread_attr_destroy(&Attr); + return true; } - uint64_t llvm::get_threadid() { #if defined(__APPLE__) // Calling "mach_thread_self()" bumps the reference count on the thread Index: lib/Support/Windows/Threading.inc =================================================================== --- lib/Support/Windows/Threading.inc +++ lib/Support/Windows/Threading.inc @@ -22,27 +22,24 @@ #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 }; +static bool llvm_execute_on_thread_impl(_beginthreadex_proc_type ThreadFunc, + void *Arg, unsigned RequestedStackSize, + bool ShouldJoin) { - HANDLE hThread = (HANDLE)::_beginthreadex(NULL, - RequestedStackSize, ThreadCallback, - ¶m, 0, NULL); + HANDLE hThread = (HANDLE)::_beginthreadex(NULL, RequestedStackSize, + ThreadFunc, Arg, 0, NULL); if (hThread) { // We actually don't care whether the wait succeeds or fails, in @@ -50,9 +47,13 @@ // 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); + if (ShouldJoin) { + (void)::WaitForSingleObject(hThread, INFINITE); + } ::CloseHandle(hThread); + return true; } + return false; } uint64_t llvm::get_threadid() { Index: unittests/Support/Threading.cpp =================================================================== --- unittests/Support/Threading.cpp +++ unittests/Support/Threading.cpp @@ -15,6 +15,31 @@ 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 +47,19 @@ 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()); +} + } // end anon namespace