Commit 54746476 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Be able to throw exceptions through the C API

in both directions, from Pyston->CAPI and CAPI->Pyston
parent b0aea029
......@@ -48,7 +48,9 @@ Box* BoxedMethodDescriptor::__call__(BoxedMethodDescriptor* self, Box* obj, Boxe
} else {
RELEASE_ASSERT(0, "0x%x", ml_flags);
}
assert(rtn);
checkAndThrowCAPIException();
assert(rtn && "should have set + thrown an exception!");
return rtn;
}
}
......@@ -15,6 +15,7 @@
#ifndef PYSTON_CAPI_TYPES_H
#define PYSTON_CAPI_TYPES_H
#include "runtime/capi.h"
#include "runtime/types.h"
namespace pyston {
......@@ -74,7 +75,9 @@ public:
} else {
RELEASE_ASSERT(0, "0x%x", self->ml_flags);
}
RELEASE_ASSERT(rtn, "need to throw an exception");
checkAndThrowCAPIException();
assert(rtn && "should have set + thrown an exception!");
return rtn;
}
};
......@@ -106,13 +109,17 @@ public:
wrapperfunc wrapper = self->descr->wrapper->wrapper;
assert(self->descr->wrapper->offset > 0);
Box* rtn;
if (flags & PyWrapperFlag_KEYWORDS) {
wrapperfunc_kwds wk = (wrapperfunc_kwds)wrapper;
return (*wk)(self->obj, args, self->descr->wrapped, kwds);
rtn = (*wk)(self->obj, args, self->descr->wrapped, kwds);
} else {
return (*wrapper)(self->obj, args, self->descr->wrapped);
rtn = (*wrapper)(self->obj, args, self->descr->wrapped);
}
abort();
checkAndThrowCAPIException();
assert(rtn && "should have set + thrown an exception!");
return rtn;
}
};
......
......@@ -32,6 +32,9 @@
namespace pyston {
namespace threading {
__thread ThreadState cur_thread_state
= { NULL, NULL, NULL }; // not sure if we need to explicitly request zero-initialization
PthreadFastMutex threading_lock;
// Certain thread examination functions won't be valid for a brief
......@@ -128,7 +131,7 @@ static void pushThreadState(pthread_t tid, ucontext_t* context) {
void* stack_end = (void*)(context->uc_mcontext.gregs[REG_RSP] + sizeof(void*));
#endif
assert(stack_start < stack_end);
thread_states.push_back(ThreadGCState(tid, context, stack_start, stack_end));
thread_states.push_back(ThreadGCState(tid, context, stack_start, stack_end, &cur_thread_state));
}
std::vector<ThreadGCState> getAllThreadStates() {
......@@ -252,6 +255,7 @@ static void* _thread_start(void* _arg) {
}
threading::GLReadRegion _glock;
assert(!PyErr_Occurred());
void* rtn = start_func(arg1, arg2, arg3);
current_threads[current_thread]->assertNoGenerators();
......@@ -350,6 +354,8 @@ void registerMainThread() {
int code = sigaction(SIGUSR2, &act, &oldact);
if (code)
err(1, NULL);
assert(!PyErr_Occurred());
}
void finishMainThread() {
......
......@@ -28,8 +28,14 @@ class Box;
namespace threading {
// Whether or not a second thread was ever started:
bool threadWasStarted();
struct ThreadState {
Box* curexc_type, *curexc_value, *curexc_traceback;
};
extern __thread ThreadState cur_thread_state;
// returns a thread id (currently, the pthread_t id)
intptr_t start_thread(void* (*start_func)(Box*, Box*, Box*), Box* arg1, Box* arg2, Box* arg3);
......@@ -45,8 +51,10 @@ struct ThreadGCState {
// in a generator, but those generators will be tracked separately.
void* stack_start, *stack_end;
ThreadGCState(pthread_t tid, ucontext_t* ucontext, void* stack_start, void* stack_end)
: tid(tid), ucontext(ucontext), stack_start(stack_start), stack_end(stack_end) {}
ThreadState* thread_state;
ThreadGCState(pthread_t tid, ucontext_t* ucontext, void* stack_start, void* stack_end, ThreadState* thread_state)
: tid(tid), ucontext(ucontext), stack_start(stack_start), stack_end(stack_end), thread_state(thread_state) {}
};
// Gets a ThreadGCState per thread, not including the thread calling this function.
// For this call to make sense, the threads all should be blocked;
......
......@@ -47,11 +47,20 @@ void collectRoots(void* start, void* end, TraceStack* stack) {
void collectOtherThreadsStacks(TraceStack* stack) {
GCVisitor v(stack);
std::vector<threading::ThreadGCState> threads = threading::getAllThreadStates();
for (threading::ThreadGCState& tstate : threads) {
collectRoots(tstate.stack_start, tstate.stack_end, stack);
collectRoots(tstate.ucontext, tstate.ucontext + 1, stack);
if (tstate.thread_state->curexc_type)
v.visit(tstate.thread_state->curexc_type);
if (tstate.thread_state->curexc_value)
v.visit(tstate.thread_state->curexc_value);
if (tstate.thread_state->curexc_traceback)
v.visit(tstate.thread_state->curexc_traceback);
}
}
......@@ -75,6 +84,14 @@ static void collectLocalStack(TraceStack* stack) {
#else
collectRoots(stack_bottom, stack_top, stack);
#endif
GCVisitor v(stack);
if (threading::cur_thread_state.curexc_type)
v.visit(threading::cur_thread_state.curexc_type);
if (threading::cur_thread_state.curexc_value)
v.visit(threading::cur_thread_state.curexc_value);
if (threading::cur_thread_state.curexc_traceback)
v.visit(threading::cur_thread_state.curexc_traceback);
}
void collectStackRoots(TraceStack* stack) {
......
......@@ -482,9 +482,28 @@ extern "C" int PyCallable_Check(PyObject* x) {
return typeLookup(x->cls, call_attr, NULL) != NULL;
}
void checkAndThrowCAPIException() {
Box* value = threading::cur_thread_state.curexc_value;
if (value) {
RELEASE_ASSERT(threading::cur_thread_state.curexc_traceback == NULL, "unsupported");
// This doesn't seem like the right behavior...
if (value->cls == tuple_cls) {
BoxedTuple* args = static_cast<BoxedTuple*>(value);
value = runtimeCall(threading::cur_thread_state.curexc_type, ArgPassSpec(0, 0, true, false), args, NULL,
NULL, NULL, NULL);
}
RELEASE_ASSERT(value->cls == threading::cur_thread_state.curexc_type, "unsupported");
PyErr_Clear();
throw value;
}
}
extern "C" void PyErr_Restore(PyObject* type, PyObject* value, PyObject* traceback) {
Py_FatalError("setting exceptions from the C API is current unimplemented");
threading::cur_thread_state.curexc_type = type;
threading::cur_thread_state.curexc_value = value;
threading::cur_thread_state.curexc_traceback = traceback;
}
extern "C" void PyErr_Clear() {
......@@ -516,9 +535,7 @@ extern "C" int PyErr_ExceptionMatches(PyObject* exc) {
}
extern "C" PyObject* PyErr_Occurred() {
// While there clearly needs to be more here, I think this is ok for now because all of the exception-setting
// functions will abort()
return NULL;
return threading::cur_thread_state.curexc_type;
}
extern "C" int PyErr_WarnEx(PyObject* category, const char* text, Py_ssize_t stacklevel) {
......
......@@ -15,12 +15,14 @@
#ifndef PYSTON_RUNTIME_CAPI_H
#define PYSTON_RUNTIME_CAPI_H
#include <cstring>
#include <string>
namespace pyston {
class BoxedModule;
BoxedModule* importTestExtension(const std::string&);
void checkAndThrowCAPIException();
}
#endif
......@@ -3336,22 +3336,40 @@ Box* typeCallInternal(BoxedFunction* f, CallRewriteArgs* rewrite_args, ArgPassSp
if (argspec.has_starargs) {
rewrite_args = NULL;
assert(argspec.num_args == 0); // doesn't need to be true, but assumed here
Box* starargs = arg1;
Box* starargs;
if (argspec.num_args == 0)
starargs = arg1;
else if (argspec.num_args == 1)
starargs = arg2;
else
abort();
assert(starargs->cls == tuple_cls);
BoxedTuple* targs = static_cast<BoxedTuple*>(starargs);
int n = targs->elts.size();
if (n >= 1)
arg1 = targs->elts[0];
if (n >= 2)
arg2 = targs->elts[1];
if (n >= 3)
arg3 = targs->elts[2];
if (n >= 4)
args = &targs->elts[3];
argspec = ArgPassSpec(n);
if (argspec.num_args == 0) {
if (n >= 1)
arg1 = targs->elts[0];
if (n >= 2)
arg2 = targs->elts[1];
if (n >= 3)
arg3 = targs->elts[2];
if (n >= 4)
args = &targs->elts[3];
} else if (argspec.num_args == 1) {
if (n >= 1)
arg2 = targs->elts[0];
if (n >= 2)
arg3 = targs->elts[1];
if (n >= 3)
args = &targs->elts[2];
} else {
abort(); // unhandled
}
argspec = ArgPassSpec(n + argspec.num_args);
}
Box* _cls = arg1;
......
# skip-if: True
# - wip
import collections
# skip-if: True
# - wip
from distutils.core import setup
......@@ -13,4 +13,7 @@ print type(os.stat("/dev/null"))
print os.path.expanduser("~") == os.environ["HOME"]
print os.path.isfile("/dev/null")
print os.path.isfile("/should_not_exist!")
OSError(1, 2, 3)
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