Permalink
Browse files

rework VM exception handling; throw OOMEs when appropriate

This rather large commit modifies the VM to use non-local returns to
throw exceptions instead of simply setting Thread::exception and
returning frame-by-frame as it used to.  This has several benefits:

 * Functions no longer need to check Thread::exception after each call
   which might throw an exception (which would be especially tedious
   and error-prone now that any function which allocates objects
   directly or indirectly might throw an OutOfMemoryError)

 * There's no need to audit the code for calls to functions which
   previously did not throw exceptions but later do

 * Performance should be improved slightly due to both the reduced
   need for conditionals and because undwinding now occurs in a single
   jump instead of a series of returns

The main disadvantages are:

 * Slightly higher overhead for entering and leaving the VM via the
   JNI and JDK methods

 * Non-local returns can make the code harder to read

 * We must be careful to register destructors for stack-allocated
   resources with the Thread so they can be called prior to a
   non-local return

The non-local return implementation is similar to setjmp/longjmp,
except it uses continuation-passing style to avoid the need for
cooperation from the C/C++ compiler.  Native C++ exceptions would have
also been an option, but that would introduce a dependence on
libstdc++, which we're trying to avoid for portability reasons.

Finally, this commit ensures that the VM throws an OutOfMemoryError
instead of aborting when it reaches its memory ceiling.  Currently, we
treat the ceiling as a soft limit and temporarily exceed it as
necessary to allow garbage collection and certain internal allocations
to succeed, but refuse to allocate any Java objects until the heap
size drops back below the ceiling.
  • Loading branch information...
1 parent 5da8b96 commit afabe8e07e1b2a5b0cdfbd0b1bed88a94ea68c44 @dicej dicej committed Dec 27, 2010
Showing with 2,148 additions and 1,288 deletions.
  1. +32 −14 src/bootimage.cpp
  2. +11 −20 src/builtin.cpp
  3. +24 −24 src/classpath-avian.cpp
  4. +16 −15 src/classpath-common.h
  5. +513 −311 src/classpath-openjdk.cpp
  6. +2 −2 src/compile-x86.S
  7. +230 −318 src/compile.cpp
  8. +8 −8 src/continuations-x86.S
  9. +23 −8 src/heap.cpp
  10. +1 −0 src/heap.h
  11. +122 −143 src/interpret.cpp
  12. +443 −178 src/jnienv.cpp
  13. +213 −156 src/machine.cpp
  14. +314 −76 src/machine.h
  15. +7 −14 src/process.cpp
  16. +127 −1 src/x86.S
  17. +62 −0 test/OutOfMemory.java
View
@@ -92,8 +92,6 @@ makeCodeImage(Thread* t, Zone* zone, BootImage* image, uint8_t* code,
(t, root(t, Machine::BootLoader),
makeByteArray(t, "%.*s", nameSize - 6, name), true);
- if (t->exception) return 0;
-
PROTECT(t, c);
if (classMethodTable(t, c)) {
@@ -138,8 +136,6 @@ makeCodeImage(Thread* t, Zone* zone, BootImage* image, uint8_t* code,
if (objectClass(t, o) == type(t, Machine::ReferenceType)) {
o = resolveClass
(t, root(t, Machine::BootLoader), referenceName(t, o));
-
- if (t->exception) return 0;
set(t, addendumPool(t, addendum),
SingletonBody + (index * BytesPerWord), o);
@@ -360,9 +356,9 @@ offset(object a, uintptr_t* b)
}
void
-writeBootImage(Thread* t, FILE* out, BootImage* image, uint8_t* code,
- unsigned codeCapacity, const char* className,
- const char* methodName, const char* methodSpec)
+writeBootImage2(Thread* t, FILE* out, BootImage* image, uint8_t* code,
+ unsigned codeCapacity, const char* className,
+ const char* methodName, const char* methodSpec)
{
Zone zone(t->m->system, t->m->heap, 64 * 1024);
@@ -373,8 +369,6 @@ writeBootImage(Thread* t, FILE* out, BootImage* image, uint8_t* code,
object constants = makeCodeImage
(t, &zone, image, code, codeMap, className, methodName, methodSpec);
- if (t->exception) return;
-
PROTECT(t, constants);
// this map will not be used when the bootimage is loaded, so
@@ -505,6 +499,23 @@ writeBootImage(Thread* t, FILE* out, BootImage* image, uint8_t* code,
}
}
+uint64_t
+writeBootImage(Thread* t, uintptr_t* arguments)
+{
+ FILE* out = reinterpret_cast<FILE*>(arguments[0]);
+ BootImage* image = reinterpret_cast<BootImage*>(arguments[1]);
+ uint8_t* code = reinterpret_cast<uint8_t*>(arguments[2]);
+ unsigned codeCapacity = arguments[3];
+ const char* className = reinterpret_cast<const char*>(arguments[4]);
+ const char* methodName = reinterpret_cast<const char*>(arguments[5]);
+ const char* methodSpec = reinterpret_cast<const char*>(arguments[6]);
+
+ writeBootImage2
+ (t, out, image, code, codeCapacity, className, methodName, methodSpec);
+
+ return 1;
+}
+
} // namespace
int
@@ -540,15 +551,22 @@ main(int ac, const char** av)
return -1;
}
- writeBootImage
- (t, output, &image, code, CodeCapacity,
- (ac > 3 ? av[3] : 0), (ac > 4 ? av[4] : 0), (ac > 5 ? av[5] : 0));
+ uintptr_t arguments[] = { reinterpret_cast<uintptr_t>(output),
+ reinterpret_cast<uintptr_t>(&image),
+ reinterpret_cast<uintptr_t>(code),
+ CodeCapacity,
+ reinterpret_cast<uintptr_t>(ac > 3 ? av[3] : 0),
+ reinterpret_cast<uintptr_t>(ac > 4 ? av[4] : 0),
+ reinterpret_cast<uintptr_t>(ac > 5 ? av[5] : 0) };
+
+ run(t, writeBootImage, arguments);
fclose(output);
if (t->exception) {
printTrace(t, t->exception);
+ return -1;
+ } else {
+ return 0;
}
-
- return 0;
}
View
@@ -33,15 +33,9 @@ search(Thread* t, object loader, object name,
replace('.', '/', s);
}
- object r = op(t, loader, n);
- if (t->exception) {
- return 0;
- }
-
- return reinterpret_cast<int64_t>(r);
+ return reinterpret_cast<int64_t>(op(t, loader, n));
} else {
- t->exception = makeThrowable(t, Machine::NullPointerExceptionType);
- return 0;
+ throwNew(t, Machine::NullPointerExceptionType);
}
}
@@ -81,7 +75,7 @@ Avian_avian_SystemClassLoader_resourceExists
object name = reinterpret_cast<object>(arguments[1]);
if (LIKELY(name)) {
- RUNTIME_ARRAY(char, n, stringLength(t, name) + 1);
+ THREAD_RUNTIME_ARRAY(t, char, n, stringLength(t, name) + 1);
stringChars(t, name, RUNTIME_ARRAY_BODY(n));
unsigned length;
@@ -92,8 +86,7 @@ Avian_avian_SystemClassLoader_resourceExists
return r;
} else {
- t->exception = makeThrowable(t, Machine::NullPointerExceptionType);
- return 0;
+ throwNew(t, Machine::NullPointerExceptionType);
}
}
@@ -114,17 +107,16 @@ Avian_avian_Machine_dumpHeap
object outputFile = reinterpret_cast<object>(*arguments);
unsigned length = stringLength(t, outputFile);
- char n[length + 1];
- stringChars(t, outputFile, n);
- FILE* out = vm::fopen(n, "wb");
+ THREAD_RUNTIME_ARRAY(t, char, n, length + 1);
+ stringChars(t, outputFile, RUNTIME_ARRAY_BODY(n));
+ FILE* out = vm::fopen(RUNTIME_ARRAY_BODY(n), "wb");
if (out) {
{ ENTER(t, Thread::ExclusiveState);
dumpHeap(t, out);
}
fclose(out);
} else {
- object message = makeString(t, "file not found: %s", n);
- t->exception = makeThrowable(t, Machine::RuntimeExceptionType, message);
+ throwNew(t, Machine::RuntimeExceptionType, "file not found: %s", n);
}
}
@@ -146,7 +138,7 @@ Avian_avian_resource_Handler_00024ResourceInputStream_getContentLength
object path = reinterpret_cast<object>(*arguments);
if (LIKELY(path)) {
- RUNTIME_ARRAY(char, p, stringLength(t, path) + 1);
+ THREAD_RUNTIME_ARRAY(t, char, p, stringLength(t, path) + 1);
stringChars(t, path, RUNTIME_ARRAY_BODY(p));
System::Region* r = t->m->bootFinder->find(RUNTIME_ARRAY_BODY(p));
@@ -170,7 +162,7 @@ Avian_avian_resource_Handler_00024ResourceInputStream_open
object path = reinterpret_cast<object>(*arguments);
if (LIKELY(path)) {
- RUNTIME_ARRAY(char, p, stringLength(t, path) + 1);
+ THREAD_RUNTIME_ARRAY(t, char, p, stringLength(t, path) + 1);
stringChars(t, path, RUNTIME_ARRAY_BODY(p));
System::Region* r = t->m->bootFinder->find(RUNTIME_ARRAY_BODY(p));
@@ -180,8 +172,7 @@ Avian_avian_resource_Handler_00024ResourceInputStream_open
return reinterpret_cast<int64_t>(r);
} else {
- t->exception = makeThrowable(t, Machine::NullPointerExceptionType);
- return 0;
+ throwNew(t, Machine::NullPointerExceptionType);
}
}
@@ -60,9 +60,7 @@ class MyClasspath : public Classpath {
(t, root(t, Machine::BootLoader), "java/lang/Thread", "run",
"(Ljava/lang/Thread;)V");
- if (t->exception == 0) {
- t->m->processor->invoke(t, method, 0, t->javaThread);
- }
+ t->m->processor->invoke(t, method, 0, t->javaThread);
}
virtual void
@@ -134,9 +132,8 @@ extern "C" JNIEXPORT int64_t JNICALL
Avian_java_lang_Object_getVMClass
(Thread* t, object, uintptr_t* arguments)
{
- object o = reinterpret_cast<object>(arguments[0]);
-
- return reinterpret_cast<int64_t>(objectClass(t, o));
+ return reinterpret_cast<int64_t>
+ (objectClass(t, reinterpret_cast<object>(arguments[0])));
}
extern "C" JNIEXPORT void JNICALL
@@ -307,12 +304,16 @@ Avian_java_lang_reflect_Method_invoke
object instance = reinterpret_cast<object>(arguments[1]);
object args = reinterpret_cast<object>(arguments[2]);
- object v = t->m->processor->invokeArray(t, method, instance, args);
- if (t->exception) {
- t->exception = makeThrowable
- (t, Machine::InvocationTargetExceptionType, 0, 0, t->exception);
- }
- return reinterpret_cast<int64_t>(v);
+ THREAD_RESOURCE0(t, {
+ if (t->exception) {
+ object exception = t->exception;
+ t->exception = makeThrowable
+ (t, Machine::InvocationTargetExceptionType, 0, 0, exception);
+ }
+ });
+
+ return reinterpret_cast<int64_t>
+ (t->m->processor->invokeArray(t, method, instance, args));
}
extern "C" JNIEXPORT int64_t JNICALL
@@ -327,12 +328,11 @@ Avian_java_lang_reflect_Array_getLength
if (LIKELY(elementSize)) {
return cast<uintptr_t>(array, BytesPerWord);
} else {
- t->exception = makeThrowable(t, Machine::IllegalArgumentExceptionType);
+ throwNew(t, Machine::IllegalArgumentExceptionType);
}
} else {
- t->exception = makeThrowable(t, Machine::NullPointerExceptionType);
+ throwNew(t, Machine::NullPointerExceptionType);
}
- return 0;
}
extern "C" JNIEXPORT int64_t JNICALL
@@ -394,7 +394,7 @@ Avian_java_lang_System_getVMProperty
PROTECT(t, found);
unsigned length = stringLength(t, name);
- RUNTIME_ARRAY(char, n, length + 1);
+ THREAD_RUNTIME_ARRAY(t, char, n, length + 1);
stringChars(t, name, RUNTIME_ARRAY_BODY(n));
int64_t r = 0;
@@ -439,8 +439,7 @@ Avian_java_lang_System_identityHashCode
if (LIKELY(o)) {
return objectHash(t, o);
} else {
- t->exception = makeThrowable(t, Machine::NullPointerExceptionType);
- return 0;
+ throwNew(t, Machine::NullPointerExceptionType);
}
}
@@ -452,7 +451,7 @@ Avian_java_lang_Runtime_load
bool mapName = arguments[1];
unsigned length = stringLength(t, name);
- RUNTIME_ARRAY(char, n, length + 1);
+ THREAD_RUNTIME_ARRAY(t, char, n, length + 1);
stringChars(t, name, RUNTIME_ARRAY_BODY(n));
loadLibrary(t, "", RUNTIME_ARRAY_BODY(n), mapName, true);
@@ -622,11 +621,13 @@ Avian_avian_Classes_defineVMClass
uint8_t* buffer = static_cast<uint8_t*>
(t->m->heap->allocate(length));
+
+ THREAD_RESOURCE2(t, uint8_t*, buffer, int, length,
+ t->m->heap->free(buffer, length));
+
memcpy(buffer, &byteArrayBody(t, b, offset), length);
- object c = defineClass(t, loader, buffer, length);
- t->m->heap->free(buffer, length);
- return reinterpret_cast<int64_t>(c);
+ return reinterpret_cast<int64_t>(defineClass(t, loader, buffer, length));
}
extern "C" JNIEXPORT void JNICALL
@@ -648,8 +649,7 @@ Avian_avian_Classes_isAssignableFrom
if (LIKELY(that)) {
return vm::isAssignableFrom(t, this_, that);
} else {
- t->exception = makeThrowable(t, Machine::NullPointerExceptionType);
- return 0;
+ throwNew(t, Machine::NullPointerExceptionType);
}
}
@@ -101,21 +101,19 @@ arrayCopy(Thread* t, object src, int32_t srcOffset, object dst,
return;
} else {
- t->exception = makeThrowable
- (t, Machine::IndexOutOfBoundsExceptionType);
- return;
+ throwNew(t, Machine::IndexOutOfBoundsExceptionType);
}
} else {
return;
}
}
}
} else {
- t->exception = makeThrowable(t, Machine::NullPointerExceptionType);
+ throwNew(t, Machine::NullPointerExceptionType);
return;
}
- t->exception = makeThrowable(t, Machine::ArrayStoreExceptionType);
+ throwNew(t, Machine::ArrayStoreExceptionType);
}
void
@@ -158,6 +156,7 @@ loadLibrary(Thread* t, const char* path, const char* name, bool mapName,
{
ACQUIRE(t, t->m->classLock);
+ char* mappedName;
unsigned nameLength = strlen(name);
if (mapName) {
const char* builtins = findProperty(t, "avian.builtins");
@@ -186,23 +185,30 @@ loadLibrary(Thread* t, const char* path, const char* name, bool mapName,
const char* suffix = t->m->system->librarySuffix();
unsigned mappedNameLength = nameLength + strlen(prefix) + strlen(suffix);
- char* mappedName = static_cast<char*>
+ mappedName = static_cast<char*>
(t->m->heap->allocate(mappedNameLength + 1));
snprintf(mappedName, mappedNameLength + 1, "%s%s%s", prefix, name, suffix);
name = mappedName;
nameLength = mappedNameLength;
+ } else {
+ mappedName = 0;
}
+ THREAD_RESOURCE2
+ (t, char*, mappedName, unsigned, nameLength, if (mappedName) {
+ t->m->heap->free(mappedName, nameLength + 1);
+ });
+
System::Library* lib = 0;
for (Tokenizer tokenizer(path, t->m->system->pathSeparator());
tokenizer.hasMore();)
{
Tokenizer::Token token(tokenizer.next());
unsigned fullNameLength = token.length + 1 + nameLength;
- RUNTIME_ARRAY(char, fullName, fullNameLength + 1);
+ THREAD_RUNTIME_ARRAY(t, char, fullName, fullNameLength + 1);
snprintf(RUNTIME_ARRAY_BODY(fullName), fullNameLength + 1,
"%*s/%s", token.length, token.s, name);
@@ -220,13 +226,8 @@ loadLibrary(Thread* t, const char* path, const char* name, bool mapName,
runOnLoadIfFound(t, lib);
}
} else {
- object message = makeString(t, "library not found: %s", name);
- t->exception = makeThrowable
- (t, Machine::UnsatisfiedLinkErrorType, message);
- }
-
- if (mapName) {
- t->m->heap->free(name, nameLength + 1);
+ throwNew(t, Machine::UnsatisfiedLinkErrorType, "library not found: %s",
+ name);
}
return lib;
@@ -264,7 +265,7 @@ makeStackTraceElement(Thread* t, object e)
object class_ = className(t, methodClass(t, traceElementMethod(t, e)));
PROTECT(t, class_);
- RUNTIME_ARRAY(char, s, byteArrayLength(t, class_));
+ THREAD_RUNTIME_ARRAY(t, char, s, byteArrayLength(t, class_));
replace('/', '.', RUNTIME_ARRAY_BODY(s),
reinterpret_cast<char*>(&byteArrayBody(t, class_, 0)));
class_ = makeString(t, "%s", s);
Oops, something went wrong.

0 comments on commit afabe8e

Please sign in to comment.