Commit 30633f23 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Add safepoints, which for now are for GIL checking

Once the GRWL is added, will also be for GC safepoints.
parent d8c0bbd8
......@@ -564,6 +564,10 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua
emitter->getBuilder()->SetInsertPoint(llvm_entry_blocks[source->cfg->getStartingBlock()]);
}
generator->unpackArguments(arg_names, cf->spec->arg_types);
// Function-entry safepoint:
// TODO might be more efficient to do post-call safepoints?
generator->doSafePoint();
} else if (entry_descriptor && block == entry_descriptor->backedge->target) {
assert(block->predecessors.size() > 1);
assert(osr_entry_block);
......@@ -653,6 +657,15 @@ static void emitBBs(IRGenState* irstate, const char* bb_type, GuardList& out_gua
}
}
for (CFGBlock* predecessor : block->predecessors) {
if (predecessor->idx > block->idx) {
// Loop safepoint:
// TODO does it matter which side of the backedge these are on?
generator->doSafePoint();
break;
}
}
generator->run(block);
const IRGenerator::EndingState& ending_st = generator->getEndingSymbolTable();
......
......@@ -2092,6 +2092,10 @@ public:
doStmt(block->body[i], exc_info);
}
}
void doSafePoint() override {
emitter.getBuilder()->CreateCall(g.funcs.allowGLReadPreemption);
}
};
IRGenerator* createIRGenerator(IRGenState* irstate, std::unordered_map<CFGBlock*, llvm::BasicBlock*>& entry_blocks,
......
......@@ -189,6 +189,7 @@ public:
virtual void copySymbolsFrom(SymbolTable* st) = 0;
virtual void run(const CFGBlock* block) = 0;
virtual EndingState getEndingSymbolTable() = 0;
virtual void doSafePoint() = 0;
};
class IREmitter;
......
......@@ -56,7 +56,10 @@ public:
} else if (llvm::GlobalVariable* gv = llvm::dyn_cast<llvm::GlobalVariable>(v)) {
// llvm::errs() << " is gv\n";
assert(gv->getLinkage() != llvm::GlobalVariable::PrivateLinkage);
r = new_module->getOrInsertGlobal(gv->getName(), gv->getType()->getElementType());
llvm::GlobalVariable* new_gv = llvm::cast<llvm::GlobalVariable>(new_module->getOrInsertGlobal(gv->getName(), gv->getType()->getElementType()));
RELEASE_ASSERT(!gv->isThreadLocal(), "I don't think MCJIT supports thread-local variables yet");
new_gv->setThreadLocalMode(gv->getThreadLocalMode());
r = new_gv;
} else if (llvm::GlobalAlias* alias = llvm::dyn_cast<llvm::GlobalAlias>(v)) {
#if LLVMREV < 209040
llvm::Value* addressee = llvm::cast<llvm::Constant>(materializeValueFor(alias->getAliasedGlobal()));
......
......@@ -30,6 +30,7 @@
#include "codegen/irgen.h"
#include "codegen/irgen/hooks.h"
#include "codegen/irgen/util.h"
#include "core/threading.h"
#include "core/types.h"
#include "runtime/float.h"
#include "runtime/gc_runtime.h"
......@@ -143,6 +144,8 @@ void initGlobalFuncs(GlobalState& g) {
g.funcs.malloc = addFunc((void*)malloc, g.i8_ptr, g.i64);
g.funcs.free = addFunc((void*)free, g.void_, g.i8_ptr);
g.funcs.allowGLReadPreemption = addFunc((void*)threading::allowGLReadPreemption, g.void_);
GET(boxCLFunction);
GET(unboxCLFunction);
GET(createUserClass);
......
......@@ -22,6 +22,8 @@ class Value;
namespace pyston {
struct GlobalFuncs {
llvm::Value* allowGLReadPreemption;
llvm::Value* printf, *my_assert, *malloc, *free;
llvm::Value* boxInt, *unboxInt, *boxFloat, *unboxFloat, *boxStringPtr, *boxCLFunction, *unboxCLFunction,
......
......@@ -147,7 +147,8 @@ static void* _thread_start(void* _arg) {
pthread_attr_t thread_attrs;
int code = pthread_getattr_np(current_thread, &thread_attrs);
RELEASE_ASSERT(code == 0, "");
if (code)
err(1, NULL);
void* stack_start;
size_t stack_size;
......@@ -275,13 +276,35 @@ void registerMainThread() {
#if THREADING_USE_GIL
static pthread_mutex_t gil = PTHREAD_MUTEX_INITIALIZER;
static std::atomic<int> threads_waiting_on_gil(0);
void acquireGLWrite() {
threads_waiting_on_gil++;
pthread_mutex_lock(&gil);
threads_waiting_on_gil--;
}
void releaseGLWrite() {
pthread_mutex_unlock(&gil);
}
#define GIL_CHECK_INTERVAL 1000
// Note: this doesn't need to be an atomic, since it should
// only be accessed by the thread that holds the gil:
int gil_check_count = 0;
void allowGLReadPreemption() {
// Can read this variable with relaxed consistency; not a huge problem if
// we accidentally read a stale value for a little while.
if (!threads_waiting_on_gil.load(std::memory_order_relaxed))
return;
gil_check_count++;
if (gil_check_count >= GIL_CHECK_INTERVAL) {
gil_check_count = 0;
releaseGLRead();
acquireGLRead();
}
}
#endif
} // namespace threading
......
......@@ -62,6 +62,7 @@ void acquireGLRead();
void releaseGLRead();
void acquireGLWrite();
void releaseGLWrite();
void allowGLReadPreemption();
// Note: promoteGL is free to drop the lock and then reacquire
void promoteGL();
void demoteGL();
......
......@@ -14,6 +14,7 @@
#include <cmath>
#include <ctime>
#include <err.h>
#include <sys/time.h>
#include "codegen/compvars.h"
......@@ -55,6 +56,9 @@ Box* timeSleep(Box* arg) {
{
threading::GLReadReleaseRegion _allow_threads;
code = nanosleep(&req, NULL);
if (code)
err(1, NULL);
}
RELEASE_ASSERT(code == 0, "%d", code);
......
from thread import start_new_thread
import time
work = []
done = []
......@@ -12,13 +11,14 @@ def run(num):
print "starting!"
nthreads = 2
N = 100000
for i in xrange(nthreads):
work.append(1000000)
work.append(N)
for i in xrange(nthreads):
t = start_new_thread(run, (1000000,))
t = start_new_thread(run, (N,))
while len(done) < nthreads:
time.sleep(0)
pass
# print work
assert sum(work) == 0
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment