mirror of
https://github.com/RPCSX/llvm.git
synced 2025-04-06 02:01:43 +00:00

D19271. Previous attempt was broken by NetBSD, so in this version I've made the fallback path generic rather than Windows specific and sent both Windows and NetBSD to it. I've also re-formatted the code some, and used an exact clone of the code in PassSupport.h for doing manual call-once using our atomics rather than rolling a new one. If this sticks, we can replace the fallback path for Windows with a Windows-specific implementation that is more reliable. Original commit message: This patch adds an llvm_call_once which is a wrapper around std::call_once on platforms where it is available and devoid of bugs. The patch also migrates the ManagedStatic mutex to be allocated using llvm_call_once. These changes are philosophically equivalent to the changes added in r219638, which were reverted due to a hang on Win32 which was the result of a bug in the Windows implementation of std::call_once. Differential Revision: http://reviews.llvm.org/D5922 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@271558 91177308-0d34-0410-b5e6-96231b3b80d8
141 lines
3.9 KiB
C++
141 lines
3.9 KiB
C++
//===-- llvm/Support/Threading.cpp- Control multithreading mode --*- C++ -*-==//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file defines helper functions for running LLVM in a multi-threaded
|
|
// environment.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/Support/Threading.h"
|
|
#include "llvm/Config/config.h"
|
|
#include "llvm/Support/Atomic.h"
|
|
#include "llvm/Support/Mutex.h"
|
|
#include "llvm/Support/thread.h"
|
|
#include <cassert>
|
|
|
|
using namespace llvm;
|
|
|
|
bool llvm::llvm_is_multithreaded() {
|
|
#if LLVM_ENABLE_THREADS != 0
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
#if LLVM_ENABLE_THREADS != 0 && defined(HAVE_PTHREAD_H)
|
|
#include <pthread.h>
|
|
|
|
struct ThreadInfo {
|
|
void (*UserFn)(void *);
|
|
void *UserData;
|
|
};
|
|
static void *ExecuteOnThread_Dispatch(void *Arg) {
|
|
ThreadInfo *TI = reinterpret_cast<ThreadInfo*>(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;
|
|
|
|
// Construct the attributes object.
|
|
if (::pthread_attr_init(&Attr) != 0)
|
|
return;
|
|
|
|
// Set the requested stack size, if given.
|
|
if (RequestedStackSize != 0) {
|
|
if (::pthread_attr_setstacksize(&Attr, RequestedStackSize) != 0)
|
|
goto error;
|
|
}
|
|
|
|
// 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);
|
|
|
|
error:
|
|
::pthread_attr_destroy(&Attr);
|
|
}
|
|
#elif LLVM_ENABLE_THREADS!=0 && defined(LLVM_ON_WIN32)
|
|
#include "Windows/WindowsSupport.h"
|
|
#include <process.h>
|
|
|
|
struct ThreadInfo {
|
|
void (*func)(void*);
|
|
void *param;
|
|
};
|
|
|
|
static unsigned __stdcall ThreadCallback(void *param) {
|
|
struct ThreadInfo *info = reinterpret_cast<struct ThreadInfo *>(param);
|
|
info->func(info->param);
|
|
|
|
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);
|
|
}
|
|
}
|
|
#else
|
|
// Support for non-Win32, non-pthread implementation.
|
|
void llvm::llvm_execute_on_thread(void (*Fn)(void*), void *UserData,
|
|
unsigned RequestedStackSize) {
|
|
(void) RequestedStackSize;
|
|
Fn(UserData);
|
|
}
|
|
|
|
#endif
|
|
|
|
void llvm::call_once(once_flag &flag, void (*fptr)(void)) {
|
|
#if LLVM_THREADING_USE_STD_CALL_ONCE
|
|
std::call_once(flag, fptr);
|
|
#else
|
|
// For other platforms we use a generic (if brittle) version based on our
|
|
// atomics.
|
|
sys::cas_flag old_val = sys::CompareAndSwap(&flag, Wait, Uninitialized);
|
|
if (old_val == Uninitialized) {
|
|
fptr();
|
|
sys::MemoryFence();
|
|
TsanIgnoreWritesBegin();
|
|
TsanHappensBefore(&flag);
|
|
flag = Done;
|
|
TsanIgnoreWritesEnd();
|
|
} else {
|
|
// Wait until any thread doing the call has finished.
|
|
sys::cas_flag tmp = flag;
|
|
sys::MemoryFence();
|
|
while (tmp != Done) {
|
|
tmp = flag;
|
|
sys::MemoryFence();
|
|
}
|
|
}
|
|
TsanHappensAfter(&flag);
|
|
#endif
|
|
}
|