mirror of
https://github.com/corda/corda.git
synced 2025-01-19 11:16:54 +00:00
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:
parent
0c464f3f84
commit
8b83de8985
@ -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);
|
||||
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user