add avian.Machine.tryNative

This function allows you to call native code such that any
SIGSEGV/SIGBUS/SIGFPE/EXC_ACCESS_VIOLATION/etc. raised by that code is
transformed into a Java exception and thrown by tryNative.  Note that
this effectively results in a longjmp out of whatever function raised
the exception, so any C++ destructors or other cleanup code will not
be run.
This commit is contained in:
Joel Dice 2014-08-19 11:08:15 -06:00
parent 0c464f3f84
commit 8b83de8985
4 changed files with 75 additions and 12 deletions

View File

@ -35,4 +35,33 @@ public abstract class Machine {
return unsafe;
}
/**
* Short version: Don't use this function -- it's evil.
*
* Long version: This is kind of a poor man's, cross-platform
* version of Microsoft's Structured Exception Handling. The idea
* is that you can call a native function with the specified
* argument such that any OS signals raised (e.g. SIGSEGV, SIGBUS,
* SIGFPE, EXC_ACCESS_VIOLATION, etc.) prior to the function
* returning are converted into the appropriate Java exception
* (e.g. NullPointerException, ArithmeticException, etc.) and thrown
* from this method. This may be useful in very specific
* circumstances, e.g. to work around a bug in a library that would
* otherwise crash your app. On the other hand, you'd be much
* better off just fixing the library if possible.
*
* Caveats: The specified function should return quickly without
* blocking, since it will block critical VM features such as
* garbage collection. The implementation is equivalent to using
* setjmp/longjmp to achieve a non-local return from the signal
* handler, meaning C++ destructors and other cleanup code might not
* be run if a signal is raised. It might melt your keyboard and
* burn your fingertips. Caveat lector.
*
* @param function a function pointer of type int64_t (*)(int64_t)
* @param argument the argument to pass to the function
* @return the return value of the function
*/
public static native long tryNative(long function, long argument);
}

View File

@ -1119,6 +1119,7 @@ class Thread {
static const unsigned ActiveFlag = 1 << 5;
static const unsigned SystemFlag = 1 << 6;
static const unsigned JoinFlag = 1 << 7;
static const unsigned TryNativeFlag = 1 << 8;
class Protector {
public:

View File

@ -309,6 +309,20 @@ extern "C" AVIAN_EXPORT void JNICALL
#endif // AVIAN_HEAPDUMP
extern "C" AVIAN_EXPORT int64_t JNICALL
Avian_avian_Machine_tryNative(Thread* t, object, uintptr_t* arguments)
{
int64_t function;
memcpy(&function, arguments, 8);
int64_t argument;
memcpy(&argument, arguments + 2, 8);
t->flags |= Thread::TryNativeFlag;
THREAD_RESOURCE0(t, t->flags &= ~Thread::TryNativeFlag);
return reinterpret_cast<int64_t (*)(int64_t)>(function)(argument);
}
extern "C" AVIAN_EXPORT void JNICALL
Avian_java_lang_Runtime_exit(Thread* t, object, uintptr_t* arguments)
{

View File

@ -8164,6 +8164,18 @@ class SignalHandler : public SignalRegistrar::Handler {
{
}
void setException(MyThread* t) {
if (ensure(t, pad(fixedSize) + traceSize(t))) {
atomicOr(&(t->flags), Thread::TracingFlag);
t->exception = makeThrowable(t, type);
atomicAnd(&(t->flags), ~Thread::TracingFlag);
} else {
// not enough memory available for a new exception and stack
// trace -- use a preallocated instance instead
t->exception = (vm::roots(t)->*exc)();
}
}
virtual bool handleSignal(void** ip,
void** frame,
void** stack,
@ -8171,8 +8183,23 @@ class SignalHandler : public SignalRegistrar::Handler {
{
MyThread* t = static_cast<MyThread*>(m->localThread->get());
if (t and t->state == Thread::ActiveState) {
GcMethod* node = methodForIp(t, *ip);
if (node) {
if (t->flags & Thread::TryNativeFlag) {
setException(t);
popResources(t);
GcContinuation* continuation;
findUnwindTarget(t, ip, frame, stack, &continuation);
t->trace->targetMethod = 0;
t->trace->nativeMethod = 0;
transition(t, *ip, *stack, continuation, t->trace);
*thread = t;
return true;
} else if (methodForIp(t, *ip)) {
// add one to the IP since findLineNumber will subtract one
// when we make the trace:
MyThread::TraceContext context(
@ -8182,22 +8209,14 @@ class SignalHandler : public SignalRegistrar::Handler {
t->continuation,
t->trace);
if (ensure(t, pad(fixedSize) + traceSize(t))) {
atomicOr(&(t->flags), Thread::TracingFlag);
t->exception = makeThrowable(t, type);
atomicAnd(&(t->flags), ~Thread::TracingFlag);
} else {
// not enough memory available for a new exception and stack
// trace -- use a preallocated instance instead
t->exception = (vm::roots(t)->*exc)();
}
setException(t);
// printTrace(t, t->exception);
GcContinuation* continuation;
findUnwindTarget(t, ip, frame, stack, &continuation);
transition(t, ip, stack, continuation, t->trace);
transition(t, *ip, *stack, continuation, t->trace);
*thread = t;