Commit 28c0c4b8 authored by Marius Wachtler's avatar Marius Wachtler

Add signal support

We check for signals on most calls to runtime functions in the llvm tier and the bjit and inside the interpreter on some additional places,
because checking on every bytecode is a too large slowdown (>10%).
parent f11cb876
# expected: fail
import unittest import unittest
from test import test_support from test import test_support
from contextlib import closing from contextlib import closing
...@@ -80,7 +79,8 @@ class InterProcessSignalTests(unittest.TestCase): ...@@ -80,7 +79,8 @@ class InterProcessSignalTests(unittest.TestCase):
# don't worry about re-setting the default handlers. # don't worry about re-setting the default handlers.
signal.signal(signal.SIGHUP, self.handlerA) signal.signal(signal.SIGHUP, self.handlerA)
signal.signal(signal.SIGUSR1, self.handlerB) signal.signal(signal.SIGUSR1, self.handlerB)
signal.signal(signal.SIGUSR2, signal.SIG_IGN) # Pyston change: pyston uses SIGUSR2 internally
# signal.signal(signal.SIGUSR2, signal.SIG_IGN)
signal.signal(signal.SIGALRM, signal.default_int_handler) signal.signal(signal.SIGALRM, signal.default_int_handler)
# Variables the signals will modify: # Variables the signals will modify:
...@@ -117,9 +117,11 @@ class InterProcessSignalTests(unittest.TestCase): ...@@ -117,9 +117,11 @@ class InterProcessSignalTests(unittest.TestCase):
if test_support.verbose: if test_support.verbose:
print "HandlerBCalled exception caught" print "HandlerBCalled exception caught"
child = ignoring_eintr(subprocess.Popen, ['kill', '-USR2', str(pid)])
if child: # Pyston change: pyston uses SIGUSR2 internally
self.wait(child) # Nothing should happen. # child = ignoring_eintr(subprocess.Popen, ['kill', '-USR2', str(pid)])
# if child:
# self.wait(child) # Nothing should happen.
try: try:
signal.alarm(1) signal.alarm(1)
......
...@@ -626,6 +626,9 @@ initsignal(void) ...@@ -626,6 +626,9 @@ initsignal(void)
old_siginthandler = PyOS_setsig(SIGINT, signal_handler); old_siginthandler = PyOS_setsig(SIGINT, signal_handler);
} }
// Pyston change: let the GC scan the handlers
PyGC_AddPotentialRoot(Handlers, sizeof(Handlers));
#ifdef SIGHUP #ifdef SIGHUP
x = PyInt_FromLong(SIGHUP); x = PyInt_FromLong(SIGHUP);
PyDict_SetItemString(d, "SIGHUP", x); PyDict_SetItemString(d, "SIGHUP", x);
...@@ -895,10 +898,6 @@ PyErr_CheckSignals(void) ...@@ -895,10 +898,6 @@ PyErr_CheckSignals(void)
if (!is_tripped) if (!is_tripped)
return 0; return 0;
// Pyston change:
Py_FatalError("TODO");
#if 0
int i; int i;
PyObject *f; PyObject *f;
...@@ -943,7 +942,6 @@ PyErr_CheckSignals(void) ...@@ -943,7 +942,6 @@ PyErr_CheckSignals(void)
Py_DECREF(result); Py_DECREF(result);
} }
} }
#endif
return 0; return 0;
} }
......
...@@ -922,43 +922,66 @@ Value ASTInterpreter::visit_stmt(AST_stmt* node) { ...@@ -922,43 +922,66 @@ Value ASTInterpreter::visit_stmt(AST_stmt* node) {
printf("\n"); printf("\n");
} }
Value rtn;
switch (node->type) { switch (node->type) {
case AST_TYPE::Assert: case AST_TYPE::Assert:
return visit_assert((AST_Assert*)node); rtn = visit_assert((AST_Assert*)node);
ASTInterpreterJitInterface::pendingCallsCheckHelper();
break;
case AST_TYPE::Assign: case AST_TYPE::Assign:
return visit_assign((AST_Assign*)node); rtn = visit_assign((AST_Assign*)node);
ASTInterpreterJitInterface::pendingCallsCheckHelper();
break;
case AST_TYPE::Delete: case AST_TYPE::Delete:
return visit_delete((AST_Delete*)node); rtn = visit_delete((AST_Delete*)node);
ASTInterpreterJitInterface::pendingCallsCheckHelper();
break;
case AST_TYPE::Exec: case AST_TYPE::Exec:
return visit_exec((AST_Exec*)node); rtn = visit_exec((AST_Exec*)node);
ASTInterpreterJitInterface::pendingCallsCheckHelper();
break;
case AST_TYPE::Expr: case AST_TYPE::Expr:
// docstrings are str constant expression statements. // docstrings are str constant expression statements.
// ignore those while interpreting. // ignore those while interpreting.
if ((((AST_Expr*)node)->value)->type != AST_TYPE::Str) if ((((AST_Expr*)node)->value)->type != AST_TYPE::Str) {
return visit_expr((AST_Expr*)node); rtn = visit_expr((AST_Expr*)node);
ASTInterpreterJitInterface::pendingCallsCheckHelper();
}
break; break;
case AST_TYPE::Pass: case AST_TYPE::Pass:
return Value(); // nothing todo ASTInterpreterJitInterface::pendingCallsCheckHelper();
break; // nothing todo
case AST_TYPE::Print: case AST_TYPE::Print:
return visit_print((AST_Print*)node); rtn = visit_print((AST_Print*)node);
ASTInterpreterJitInterface::pendingCallsCheckHelper();
break;
case AST_TYPE::Raise: case AST_TYPE::Raise:
return visit_raise((AST_Raise*)node); rtn = visit_raise((AST_Raise*)node);
ASTInterpreterJitInterface::pendingCallsCheckHelper();
break;
case AST_TYPE::Return: case AST_TYPE::Return:
return visit_return((AST_Return*)node); rtn = visit_return((AST_Return*)node);
ASTInterpreterJitInterface::pendingCallsCheckHelper();
break;
case AST_TYPE::Global: case AST_TYPE::Global:
return visit_global((AST_Global*)node); rtn = visit_global((AST_Global*)node);
ASTInterpreterJitInterface::pendingCallsCheckHelper();
break;
// pseudo // pseudo
case AST_TYPE::Branch: case AST_TYPE::Branch:
return visit_branch((AST_Branch*)node); rtn = visit_branch((AST_Branch*)node);
break;
case AST_TYPE::Jump: case AST_TYPE::Jump:
return visit_jump((AST_Jump*)node); rtn = visit_jump((AST_Jump*)node);
break;
case AST_TYPE::Invoke: case AST_TYPE::Invoke:
return visit_invoke((AST_Invoke*)node); rtn = visit_invoke((AST_Invoke*)node);
break;
default: default:
RELEASE_ASSERT(0, "not implemented"); RELEASE_ASSERT(0, "not implemented");
}; };
return Value(); return rtn;
} }
Value ASTInterpreter::visit_return(AST_Return* node) { Value ASTInterpreter::visit_return(AST_Return* node) {
...@@ -1652,6 +1675,11 @@ Box* ASTInterpreterJitInterface::landingpadHelper(void* _interpreter) { ...@@ -1652,6 +1675,11 @@ Box* ASTInterpreterJitInterface::landingpadHelper(void* _interpreter) {
return rtn; return rtn;
} }
void ASTInterpreterJitInterface::pendingCallsCheckHelper() {
if (unlikely(_pendingcalls_to_do))
makePendingCalls();
}
Box* ASTInterpreterJitInterface::setExcInfoHelper(void* _interpreter, Box* type, Box* value, Box* traceback) { Box* ASTInterpreterJitInterface::setExcInfoHelper(void* _interpreter, Box* type, Box* value, Box* traceback) {
ASTInterpreter* interpreter = (ASTInterpreter*)_interpreter; ASTInterpreter* interpreter = (ASTInterpreter*)_interpreter;
interpreter->getFrameInfo()->exc = ExcInfo(type, value, traceback); interpreter->getFrameInfo()->exc = ExcInfo(type, value, traceback);
......
...@@ -45,6 +45,7 @@ struct ASTInterpreterJitInterface { ...@@ -45,6 +45,7 @@ struct ASTInterpreterJitInterface {
static Box* derefHelper(void* interp, InternedString s); static Box* derefHelper(void* interp, InternedString s);
static Box* doOSRHelper(void* interp, AST_Jump* node); static Box* doOSRHelper(void* interp, AST_Jump* node);
static Box* landingpadHelper(void* interp); static Box* landingpadHelper(void* interp);
static void pendingCallsCheckHelper();
static Box* setExcInfoHelper(void* interp, Box* type, Box* value, Box* traceback); static Box* setExcInfoHelper(void* interp, Box* type, Box* value, Box* traceback);
static void setLocalClosureHelper(void* interp, long vreg, InternedString id, Box* v); static void setLocalClosureHelper(void* interp, long vreg, InternedString id, Box* v);
static Box* uncacheExcInfoHelper(void* interp); static Box* uncacheExcInfoHelper(void* interp);
......
...@@ -474,6 +474,10 @@ void JitFragmentWriter::emitOSRPoint(AST_Jump* node) { ...@@ -474,6 +474,10 @@ void JitFragmentWriter::emitOSRPoint(AST_Jump* node) {
addAction([=]() { _emitOSRPoint(result, node_var); }, { result, node_var, getInterp() }, ActionType::NORMAL); addAction([=]() { _emitOSRPoint(result, node_var); }, { result, node_var, getInterp() }, ActionType::NORMAL);
} }
void JitFragmentWriter::emitPendingCallsCheck() {
call(false, (void*)ASTInterpreterJitInterface::pendingCallsCheckHelper);
}
void JitFragmentWriter::emitPrint(RewriterVar* dest, RewriterVar* var, bool nl) { void JitFragmentWriter::emitPrint(RewriterVar* dest, RewriterVar* var, bool nl) {
if (!dest) if (!dest)
dest = call(false, (void*)getSysStdout); dest = call(false, (void*)getSysStdout);
...@@ -696,12 +700,17 @@ RewriterVar* JitFragmentWriter::emitPPCall(void* func_addr, llvm::ArrayRef<Rewri ...@@ -696,12 +700,17 @@ RewriterVar* JitFragmentWriter::emitPPCall(void* func_addr, llvm::ArrayRef<Rewri
RewriterVar* obj_cls_var = result->getAttr(offsetof(Box, cls)); RewriterVar* obj_cls_var = result->getAttr(offsetof(Box, cls));
addAction([=]() { _emitRecordType(type_recorder_var, obj_cls_var); }, { type_recorder_var, obj_cls_var }, addAction([=]() { _emitRecordType(type_recorder_var, obj_cls_var); }, { type_recorder_var, obj_cls_var },
ActionType::NORMAL); ActionType::NORMAL);
emitPendingCallsCheck();
return result; return result;
} }
emitPendingCallsCheck();
return result; return result;
#else #else
assert(args_vec.size() < 7); assert(args_vec.size() < 7);
return call(false, func_addr, args_vec); RewriterVar* result = call(false, func_addr, args_vec);
emitPendingCallsCheck();
return result;
#endif #endif
} }
......
...@@ -246,6 +246,7 @@ public: ...@@ -246,6 +246,7 @@ public:
void emitExec(RewriterVar* code, RewriterVar* globals, RewriterVar* locals, FutureFlags flags); void emitExec(RewriterVar* code, RewriterVar* globals, RewriterVar* locals, FutureFlags flags);
void emitJump(CFGBlock* b); void emitJump(CFGBlock* b);
void emitOSRPoint(AST_Jump* node); void emitOSRPoint(AST_Jump* node);
void emitPendingCallsCheck();
void emitPrint(RewriterVar* dest, RewriterVar* var, bool nl); void emitPrint(RewriterVar* dest, RewriterVar* var, bool nl);
void emitRaise0(); void emitRaise0();
void emitRaise3(RewriterVar* arg0, RewriterVar* arg1, RewriterVar* arg2); void emitRaise3(RewriterVar* arg0, RewriterVar* arg1, RewriterVar* arg2);
......
...@@ -378,16 +378,6 @@ static void handle_sigprof_investigate_stattimer(int signum) { ...@@ -378,16 +378,6 @@ static void handle_sigprof_investigate_stattimer(int signum) {
} }
#endif #endif
static void handle_sigint(int signum) {
assert(signum == SIGINT);
// TODO: this should set a flag saying a KeyboardInterrupt is pending.
// For now, just call abort(), so that we get a traceback at least.
fprintf(stderr, "SIGINT!\n");
joinRuntime();
Stats::dump(false);
abort();
}
void initCodegen() { void initCodegen() {
llvm::InitializeNativeTarget(); llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter(); llvm::InitializeNativeTargetAsmPrinter();
...@@ -481,9 +471,8 @@ void initCodegen() { ...@@ -481,9 +471,8 @@ void initCodegen() {
setupRuntime(); setupRuntime();
// signal(SIGFPE, &handle_sigfpe); // signal(SIGFPE, &handle_sigfpe);
signal(SIGUSR1, &handle_sigusr1); // signal(SIGUSR1, &handle_sigusr1);
signal(SIGINT, &handle_sigint);
#if ENABLE_SAMPLING_PROFILER #if ENABLE_SAMPLING_PROFILER
struct itimerval prof_timer; struct itimerval prof_timer;
......
...@@ -347,11 +347,53 @@ private: ...@@ -347,11 +347,53 @@ private:
llvm::BasicBlock*& curblock; llvm::BasicBlock*& curblock;
IRGenerator* irgenerator; IRGenerator* irgenerator;
void emitPendingCallsCheck(llvm::BasicBlock* exc_dest) {
auto&& builder = *getBuilder();
llvm::GlobalVariable* pendingcalls_to_do_gv = g.cur_module->getGlobalVariable("_pendingcalls_to_do");
if (!pendingcalls_to_do_gv) {
static_assert(sizeof(_pendingcalls_to_do) == 4, "");
pendingcalls_to_do_gv = new llvm::GlobalVariable(
*g.cur_module, g.i32, false, llvm::GlobalValue::ExternalLinkage, 0, "_pendingcalls_to_do");
pendingcalls_to_do_gv->setAlignment(4);
}
llvm::BasicBlock* cur_block = builder.GetInsertBlock();
llvm::BasicBlock* pendingcalls_set = createBasicBlock("_pendingcalls_set");
pendingcalls_set->moveAfter(cur_block);
llvm::BasicBlock* join_block = createBasicBlock("continue_after_pendingcalls_check");
join_block->moveAfter(pendingcalls_set);
llvm::Value* pendingcalls_to_do_val = builder.CreateLoad(pendingcalls_to_do_gv, true /* volatile */);
llvm::Value* is_zero
= builder.CreateICmpEQ(pendingcalls_to_do_val, getConstantInt(0, pendingcalls_to_do_val->getType()));
llvm::Metadata* md_vals[]
= { llvm::MDString::get(g.context, "branch_weights"), llvm::ConstantAsMetadata::get(getConstantInt(1000)),
llvm::ConstantAsMetadata::get(getConstantInt(1)) };
llvm::MDNode* branch_weights = llvm::MDNode::get(g.context, llvm::ArrayRef<llvm::Metadata*>(md_vals));
builder.CreateCondBr(is_zero, join_block, pendingcalls_set, branch_weights);
{
setCurrentBasicBlock(pendingcalls_set);
if (exc_dest) {
builder.CreateInvoke(g.funcs.makePendingCalls, join_block, exc_dest);
} else {
builder.CreateCall(g.funcs.makePendingCalls);
builder.CreateBr(join_block);
}
}
cur_block = join_block;
setCurrentBasicBlock(join_block);
}
llvm::CallSite emitCall(const UnwindInfo& unw_info, llvm::Value* callee, const std::vector<llvm::Value*>& args, llvm::CallSite emitCall(const UnwindInfo& unw_info, llvm::Value* callee, const std::vector<llvm::Value*>& args,
ExceptionStyle target_exception_style) { ExceptionStyle target_exception_style) {
llvm::Value* stmt = unw_info.current_stmt ? embedRelocatablePtr(unw_info.current_stmt, g.llvm_aststmt_type_ptr) emitSetCurrentStmt(unw_info.current_stmt);
: getNullPtr(g.llvm_aststmt_type_ptr);
getBuilder()->CreateStore(stmt, irstate->getStmtVar());
if (target_exception_style == CXX && (unw_info.hasHandler() || irstate->getExceptionStyle() == CAPI)) { if (target_exception_style == CXX && (unw_info.hasHandler() || irstate->getExceptionStyle() == CAPI)) {
// Create the invoke: // Create the invoke:
...@@ -375,9 +417,13 @@ private: ...@@ -375,9 +417,13 @@ private:
// Normal case: // Normal case:
getBuilder()->SetInsertPoint(normal_dest); getBuilder()->SetInsertPoint(normal_dest);
curblock = normal_dest; curblock = normal_dest;
emitPendingCallsCheck(exc_dest);
return rtn; return rtn;
} else { } else {
llvm::CallInst* cs = getBuilder()->CreateCall(callee, args); llvm::CallInst* cs = getBuilder()->CreateCall(callee, args);
if (target_exception_style == CXX)
emitPendingCallsCheck(NULL);
return cs; return cs;
} }
} }
...@@ -479,6 +525,15 @@ public: ...@@ -479,6 +525,15 @@ public:
return llvm::BasicBlock::Create(g.context, name, irstate->getLLVMFunction()); return llvm::BasicBlock::Create(g.context, name, irstate->getLLVMFunction());
} }
// Our current frame introspection approach requires that we update the currently executed stmt before doing a call
// to a function which could throw an exception, inspect the python call frame,...
// Only patchpoint don't need to set the current statement because the stmt will be inluded in the stackmap args.
void emitSetCurrentStmt(AST_stmt* stmt) {
getBuilder()->CreateStore(stmt ? embedRelocatablePtr(stmt, g.llvm_aststmt_type_ptr)
: getNullPtr(g.llvm_aststmt_type_ptr),
irstate->getStmtVar());
}
llvm::Value* createCall(const UnwindInfo& unw_info, llvm::Value* callee, const std::vector<llvm::Value*>& args, llvm::Value* createCall(const UnwindInfo& unw_info, llvm::Value* callee, const std::vector<llvm::Value*>& args,
ExceptionStyle target_exception_style = CXX) override { ExceptionStyle target_exception_style = CXX) override {
#ifndef NDEBUG #ifndef NDEBUG
...@@ -2137,7 +2192,7 @@ private: ...@@ -2137,7 +2192,7 @@ private:
// Don't call deinitFrame when this is a OSR function because the interpreter will call it // Don't call deinitFrame when this is a OSR function because the interpreter will call it
if (!irstate->getCurFunction()->entry_descriptor) if (!irstate->getCurFunction()->entry_descriptor)
emitter.createCall(unw_info, g.funcs.deinitFrame, irstate->getFrameInfoVar()); emitter.getBuilder()->CreateCall(g.funcs.deinitFrame, irstate->getFrameInfoVar());
for (auto& p : symbol_table) { for (auto& p : symbol_table) {
p.second->decvref(emitter); p.second->decvref(emitter);
...@@ -2878,9 +2933,10 @@ public: ...@@ -2878,9 +2933,10 @@ public:
} }
void doSafePoint(AST_stmt* next_statement) override { void doSafePoint(AST_stmt* next_statement) override {
// Unwind info is always needed in allowGLReadPreemption if it has any chance of // We need to setup frame introspection by updating the current stmt because we can run can run arbitrary code
// running arbitrary code like finalizers. // like finalizers inside allowGLReadPreemption.
emitter.createCall(UnwindInfo(next_statement, NULL), g.funcs.allowGLReadPreemption); emitter.emitSetCurrentStmt(next_statement);
emitter.getBuilder()->CreateCall(g.funcs.allowGLReadPreemption);
} }
// Create a (or reuse an existing) block that will catch a CAPI exception, and then forward // Create a (or reuse an existing) block that will catch a CAPI exception, and then forward
...@@ -2902,7 +2958,8 @@ public: ...@@ -2902,7 +2958,8 @@ public:
assert(!phi_node); assert(!phi_node);
phi_node = emitter.getBuilder()->CreatePHI(g.llvm_aststmt_type_ptr, 0); phi_node = emitter.getBuilder()->CreatePHI(g.llvm_aststmt_type_ptr, 0);
emitter.createCall(UnwindInfo(current_stmt, NULL), g.funcs.caughtCapiException, emitter.emitSetCurrentStmt(current_stmt);
emitter.getBuilder()->CreateCall(g.funcs.caughtCapiException,
{ phi_node, embedRelocatablePtr(irstate->getSourceInfo(), g.i8_ptr) }); { phi_node, embedRelocatablePtr(irstate->getSourceInfo(), g.i8_ptr) });
if (!final_dest) { if (!final_dest) {
...@@ -2911,7 +2968,7 @@ public: ...@@ -2911,7 +2968,7 @@ public:
emitter.getBuilder()->CreateCall(g.funcs.reraiseCapiExcAsCxx); emitter.getBuilder()->CreateCall(g.funcs.reraiseCapiExcAsCxx);
emitter.getBuilder()->CreateUnreachable(); emitter.getBuilder()->CreateUnreachable();
} else { } else {
emitter.createCall(UnwindInfo(current_stmt, NULL), g.funcs.deinitFrame, irstate->getFrameInfoVar()); emitter.getBuilder()->CreateCall(g.funcs.deinitFrame, irstate->getFrameInfoVar());
emitter.getBuilder()->CreateRet(getNullPtr(g.llvm_value_type_ptr)); emitter.getBuilder()->CreateRet(getNullPtr(g.llvm_value_type_ptr));
} }
} else { } else {
......
...@@ -200,6 +200,7 @@ void initGlobalFuncs(GlobalState& g) { ...@@ -200,6 +200,7 @@ void initGlobalFuncs(GlobalState& g) {
GET(createSet); GET(createSet);
GET(initFrame); GET(initFrame);
GET(deinitFrame); GET(deinitFrame);
GET(makePendingCalls);
GET(getattr); GET(getattr);
GET(getattr_capi); GET(getattr_capi);
......
...@@ -34,7 +34,7 @@ struct GlobalFuncs { ...@@ -34,7 +34,7 @@ struct GlobalFuncs {
llvm::Value* boxInt, *unboxInt, *boxFloat, *unboxFloat, *createFunctionFromMetadata, *getFunctionMetadata, llvm::Value* boxInt, *unboxInt, *boxFloat, *unboxFloat, *createFunctionFromMetadata, *getFunctionMetadata,
*boxInstanceMethod, *boxBool, *unboxBool, *createTuple, *createDict, *createList, *createSlice, *boxInstanceMethod, *boxBool, *unboxBool, *createTuple, *createDict, *createList, *createSlice,
*createUserClass, *createClosure, *createGenerator, *createSet, *initFrame, *deinitFrame; *createUserClass, *createClosure, *createGenerator, *createSet, *initFrame, *deinitFrame, *makePendingCalls;
llvm::Value* getattr, *getattr_capi, *setattr, *delattr, *delitem, *delGlobal, *nonzero, *binop, *compare, llvm::Value* getattr, *getattr_capi, *setattr, *delattr, *delitem, *delGlobal, *nonzero, *binop, *compare,
*augbinop, *unboxedLen, *getitem, *getitem_capi, *getclsattr, *getGlobal, *setitem, *unaryop, *import, *augbinop, *unboxedLen, *getitem, *getitem_capi, *getclsattr, *getGlobal, *setitem, *unaryop, *import,
*importFrom, *importStar, *repr, *exceptionMatches, *yield, *getiterHelper, *hasnext, *setGlobal, *apply_slice; *importFrom, *importStar, *repr, *exceptionMatches, *yield, *getiterHelper, *hasnext, *setGlobal, *apply_slice;
......
...@@ -497,9 +497,13 @@ static void* find_stack() { ...@@ -497,9 +497,13 @@ static void* find_stack() {
return NULL; /* not found =^P */ return NULL; /* not found =^P */
} }
static long main_thread_id;
void registerMainThread() { void registerMainThread() {
LOCK_REGION(&threading_lock); LOCK_REGION(&threading_lock);
main_thread_id = pthread_self();
assert(!current_internal_thread_state); assert(!current_internal_thread_state);
current_internal_thread_state = new ThreadStateInternal(find_stack(), pthread_self(), &cur_thread_state); current_internal_thread_state = new ThreadStateInternal(find_stack(), pthread_self(), &cur_thread_state);
current_threads[pthread_self()] = current_internal_thread_state; current_threads[pthread_self()] = current_internal_thread_state;
...@@ -524,6 +528,10 @@ void finishMainThread() { ...@@ -524,6 +528,10 @@ void finishMainThread() {
// TODO maybe this is the place to wait for non-daemon threads? // TODO maybe this is the place to wait for non-daemon threads?
} }
bool isMainThread() {
return pthread_self() == main_thread_id;
}
// For the "AllowThreads" regions, let's save the thread state at the beginning of the region. // For the "AllowThreads" regions, let's save the thread state at the beginning of the region.
// This means that the thread won't get interrupted by the signals we would otherwise need to // This means that the thread won't get interrupted by the signals we would otherwise need to
......
...@@ -51,6 +51,8 @@ intptr_t start_thread(void* (*start_func)(Box*, Box*, Box*), Box* arg1, Box* arg ...@@ -51,6 +51,8 @@ intptr_t start_thread(void* (*start_func)(Box*, Box*, Box*), Box* arg1, Box* arg
void registerMainThread(); void registerMainThread();
void finishMainThread(); void finishMainThread();
bool isMainThread();
// Hook for the GC; will visit all the threads (including the current one), visiting their // Hook for the GC; will visit all the threads (including the current one), visiting their
// stacks and thread-local PyThreadState objects // stacks and thread-local PyThreadState objects
void visitAllStacks(gc::GCVisitor* v); void visitAllStacks(gc::GCVisitor* v);
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <string.h> #include <string.h>
#include "Python.h" #include "Python.h"
#include "pythread.h"
#include "codegen/cpython_ast.h" #include "codegen/cpython_ast.h"
#include "grammar.h" #include "grammar.h"
...@@ -1547,9 +1548,126 @@ extern "C" PyOS_sighandler_t PyOS_setsig(int sig, PyOS_sighandler_t handler) noe ...@@ -1547,9 +1548,126 @@ extern "C" PyOS_sighandler_t PyOS_setsig(int sig, PyOS_sighandler_t handler) noe
#endif #endif
} }
static PyThread_type_lock pending_lock = 0; /* for pending calls */
/* The WITH_THREAD implementation is thread-safe. It allows
scheduling to be made from any thread, and even from an executing
callback.
*/
#define NPENDINGCALLS 32
static struct {
int (*func)(void*);
void* arg;
} pendingcalls[NPENDINGCALLS];
static int pendingfirst = 0;
static int pendinglast = 0;
// Pyston change
// static volatile int pendingcalls_to_do = 1; /* trigger initialization of lock */
extern "C" {
volatile int _pendingcalls_to_do = 1;
}
static char pendingbusy = 0;
extern "C" int Py_AddPendingCall(int (*func)(void*), void* arg) noexcept { extern "C" int Py_AddPendingCall(int (*func)(void*), void* arg) noexcept {
fatalOrError(PyExc_NotImplementedError, "unimplemented"); int i, j, result = 0;
PyThread_type_lock lock = pending_lock;
/* try a few times for the lock. Since this mechanism is used
* for signal handling (on the main thread), there is a (slim)
* chance that a signal is delivered on the same thread while we
* hold the lock during the Py_MakePendingCalls() function.
* This avoids a deadlock in that case.
* Note that signals can be delivered on any thread. In particular,
* on Windows, a SIGINT is delivered on a system-created worker
* thread.
* We also check for lock being NULL, in the unlikely case that
* this function is called before any bytecode evaluation takes place.
*/
if (lock != NULL) {
for (i = 0; i < 100; i++) {
if (PyThread_acquire_lock(lock, NOWAIT_LOCK))
break;
}
if (i == 100)
return -1; return -1;
}
i = pendinglast;
j = (i + 1) % NPENDINGCALLS;
if (j == pendingfirst) {
result = -1; /* Queue full */
} else {
pendingcalls[i].func = func;
pendingcalls[i].arg = arg;
pendinglast = j;
}
/* signal main loop */
// Pyston change: we don't have a _Py_Ticker
// _Py_Ticker = 0;
_pendingcalls_to_do = 1;
if (lock != NULL)
PyThread_release_lock(lock);
return result;
}
extern "C" int Py_MakePendingCalls(void) noexcept {
int i;
int r = 0;
if (!pending_lock) {
/* initial allocation of the lock */
pending_lock = PyThread_allocate_lock();
if (pending_lock == NULL)
return -1;
// Pyston change: we could potentialy store a python object inside the arg field
PyGC_AddPotentialRoot(pendingcalls, sizeof(pendingcalls));
}
/* only service pending calls on main thread */
// Pyston change:
// if (main_thread && PyThread_get_thread_ident() != main_thread)
if (!threading::isMainThread())
return 0;
/* don't perform recursive pending calls */
if (pendingbusy)
return 0;
pendingbusy = 1;
/* perform a bounded number of calls, in case of recursion */
for (i = 0; i < NPENDINGCALLS; i++) {
int j;
int (*func)(void*);
void* arg = NULL;
/* pop one item off the queue while holding the lock */
PyThread_acquire_lock(pending_lock, WAIT_LOCK);
j = pendingfirst;
if (j == pendinglast) {
func = NULL; /* Queue empty */
} else {
func = pendingcalls[j].func;
arg = pendingcalls[j].arg;
pendingfirst = (j + 1) % NPENDINGCALLS;
}
_pendingcalls_to_do = pendingfirst != pendinglast;
PyThread_release_lock(pending_lock);
/* having released the lock, perform the callback */
if (func == NULL)
break;
r = func(arg);
if (r)
break;
}
pendingbusy = 0;
return r;
}
extern "C" void makePendingCalls() {
int ret = Py_MakePendingCalls();
if (ret != 0)
throwCAPIException();
} }
extern "C" PyObject* _PyImport_FixupExtension(char* name, char* filename) noexcept { extern "C" PyObject* _PyImport_FixupExtension(char* name, char* filename) noexcept {
...@@ -1716,6 +1834,16 @@ extern "C" void PyEval_RestoreThread(PyThreadState* tstate) noexcept { ...@@ -1716,6 +1834,16 @@ extern "C" void PyEval_RestoreThread(PyThreadState* tstate) noexcept {
endAllowThreads(); endAllowThreads();
} }
extern "C" struct _frame* PyEval_GetFrame(void) noexcept {
Box* frame = NULL;
try {
frame = getFrame(0);
} catch (ExcInfo) {
RELEASE_ASSERT(0, "untested");
}
return (struct _frame*)frame;
}
extern "C" char* PyModule_GetName(PyObject* m) noexcept { extern "C" char* PyModule_GetName(PyObject* m) noexcept {
PyObject* d; PyObject* d;
PyObject* nameobj; PyObject* nameobj;
......
...@@ -74,6 +74,7 @@ void force() { ...@@ -74,6 +74,7 @@ void force() {
FORCE(decodeUTF8StringPtr); FORCE(decodeUTF8StringPtr);
FORCE(initFrame); FORCE(initFrame);
FORCE(deinitFrame); FORCE(deinitFrame);
FORCE(makePendingCalls);
FORCE(getattr); FORCE(getattr);
FORCE(getattr_capi); FORCE(getattr_capi);
......
...@@ -170,6 +170,7 @@ extern "C" Box* createDict(); ...@@ -170,6 +170,7 @@ extern "C" Box* createDict();
extern "C" Box* createList(); extern "C" Box* createList();
extern "C" Box* createSlice(Box* start, Box* stop, Box* step); extern "C" Box* createSlice(Box* start, Box* stop, Box* step);
extern "C" Box* createTuple(int64_t nelts, Box** elts); extern "C" Box* createTuple(int64_t nelts, Box** elts);
extern "C" void makePendingCalls();
Box* objectStr(Box*); Box* objectStr(Box*);
Box* objectRepr(Box*); Box* objectRepr(Box*);
...@@ -1181,6 +1182,8 @@ inline Box*& getArg(int idx, Box*& arg1, Box*& arg2, Box*& arg3, Box** args) { ...@@ -1181,6 +1182,8 @@ inline Box*& getArg(int idx, Box*& arg1, Box*& arg2, Box*& arg3, Box** args) {
return arg3; return arg3;
return args[idx - 3]; return args[idx - 3];
} }
extern "C" volatile int _pendingcalls_to_do;
} }
#endif #endif
...@@ -183,7 +183,6 @@ test_scope eval of code object from existing function (not currentl ...@@ -183,7 +183,6 @@ test_scope eval of code object from existing function (not currentl
test_scriptpackages [unknown] test_scriptpackages [unknown]
test_shelve [unknown] test_shelve [unknown]
test_shlex [unknown] test_shlex [unknown]
test_signal [unknown]
test_site [unknown] test_site [unknown]
test_smtpnet [unknown] test_smtpnet [unknown]
test_socketserver [unknown] test_socketserver [unknown]
......
...@@ -6,3 +6,23 @@ for k in sorted(dir(signal)): ...@@ -6,3 +6,23 @@ for k in sorted(dir(signal)):
print k, getattr(signal, k) print k, getattr(signal, k)
print hasattr(signal, "alarm") print hasattr(signal, "alarm")
import time
import signal
def sig_handler(signum, stack):
print "inside sig_handler"
import sys, traceback
traceback.print_stack(stack)
sys.exit(0)
def f(lst):
signal.signal(signal.SIGALRM, sig_handler)
signal.setitimer(signal.ITIMER_REAL, 2, 1)
for x in lst:
time.sleep(x) #1
time.sleep(x) #2
f([0] * 100 + [10])
assert False, "shuld not get executed"
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