From b618ab40bebb8782ac884bc9c7cb9abbfddcd810 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Tue, 15 Mar 2011 17:20:44 -0600 Subject: [PATCH 01/50] fix OS X PowerPC parameter passing We were not always placing parameters in the correct stack positions in the PowerPC implementations of dynamicCall and vmNativeCall. In particular, the first stack slot used to hold a parameter depends on the sizes and types of the preceding parameters which are passed in registers. --- src/powerpc.S | 73 ++++++++++++++++++++++++++------------------------- src/powerpc.h | 5 ++-- 2 files changed, 40 insertions(+), 38 deletions(-) diff --git a/src/powerpc.S b/src/powerpc.S index 96bf382cfb..5a075e1a11 100644 --- a/src/powerpc.S +++ b/src/powerpc.S @@ -36,13 +36,14 @@ GLOBAL(vmNativeCall): mflr r0 stw r0,RETURN_ADDRESS_OFFSET(r1) - // r3 aka r13: function - // r4 : stackTotal - // r5 : memoryTable - // r6 : memoryCount - // r7 : gprTable - // r8 : fprTable - // r9 aka r14: returnType + // r3 aka r13: function + // r4 : stackTotal + // r5 : memoryTable + // r6 : memoryCount + // r7 : memoryBase + // r8 : gprTable + // r9 : fprTable + // r10 aka r14: returnType // r15 : stack frame size // r16 : temporary @@ -51,34 +52,34 @@ GLOBAL(vmNativeCall): // allocate stack space, adding room for callee-saved registers and // scratch space for copying a FP return value into GPRs - subfic r10,r4,-48 - stwux r1,r1,r10 + subfic r11,r4,-48 + stwux r1,r1,r11 // save callee-saved registers used for local variables - add r10,r4,r1 + add r11,r4,r1 // save registers used for local variables - stw r13,0(r10) - stw r14,4(r10) - stw r15,8(r10) - stw r16,12(r10) - stw r17,16(r10) - stw r18,20(r10) - stw r19,24(r10) + stw r13,0(r11) + stw r14,4(r11) + stw r15,8(r11) + stw r16,12(r11) + stw r17,16(r11) + stw r18,20(r11) + stw r19,24(r11) // remember where we saved the local variables - mr r19,r10 + mr r19,r11 // save our argument registers so we can clobber them mr r13,r3 - mr r14,r9 + mr r14,r10 li r16,0 b LOCAL(test) LOCAL(loop): lwzx r17,r16,r5 - addi r18,r16,MEMORY_BASE + add r18,r16,r7 stwx r17,r18,r1 addi r16,r16,BYTES_PER_WORD @@ -87,33 +88,33 @@ LOCAL(test): blt LOCAL(loop) // do we need to load the floating point registers? - cmpwi r8,0 + cmpwi r9,0 beq LOCAL(gpr) // yes, we do - lfd f1,0(r8) - lfd f2,8(r8) - lfd f3,16(r8) - lfd f4,24(r8) - lfd f5,32(r8) - lfd f6,40(r8) - lfd f7,48(r8) - lfd f8,56(r8) + lfd f1,0(r9) + lfd f2,8(r9) + lfd f3,16(r9) + lfd f4,24(r9) + lfd f5,32(r9) + lfd f6,40(r9) + lfd f7,48(r9) + lfd f8,56(r9) #ifdef __APPLE__ - lfd f9,64(r8) - lfd f10,72(r8) - lfd f11,80(r8) - lfd f12,88(r8) - lfd f13,96(r8) + lfd f9,64(r9) + lfd f10,72(r9) + lfd f11,80(r9) + lfd f12,88(r9) + lfd f13,96(r9) #endif LOCAL(gpr): // do we need to load the general-purpose registers? - cmpwi r7,0 + cmpwi r8,0 beq LOCAL(call) // yes, we do - mr r16,r7 + mr r16,r8 lwz r3,0(r16) lwz r4,4(r16) lwz r5,8(r16) diff --git a/src/powerpc.h b/src/powerpc.h index 5fe4543cc7..9b877719d0 100644 --- a/src/powerpc.h +++ b/src/powerpc.h @@ -57,8 +57,8 @@ extern "C" uint64_t vmNativeCall(void* function, unsigned stackTotal, void* memoryTable, - unsigned memoryCount, void* gprTable, void* fprTable, - unsigned returnType); + unsigned memoryCount, unsigned memoryBase, + void* gprTable, void* fprTable, unsigned returnType); namespace vm { @@ -239,6 +239,7 @@ dynamicCall(void* function, uintptr_t* arguments, uint8_t* argumentTypes, (function, (((1 + stackSkip + stackIndex) * BytesPerWord) + LinkageArea + 15) & -16, stack, stackIndex * BytesPerWord, + LinkageArea + (stackSkip * BytesPerWord), (gprIndex ? gprTable : 0), (fprIndex ? fprTable : 0), returnType); } From b5192ae7a3f71e2e5023e739a8ea1cce3cc90c1e Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Tue, 12 Jul 2011 14:15:43 -0600 Subject: [PATCH 02/50] fix Thread.interrupt and Thread.interrupted These were both totally broken; the latter wasn't even implemented. This commit fixes/implements them and adds a simple test to exercise them. --- classpath/java/lang/Thread.java | 12 +++++++++-- src/classpath-avian.cpp | 9 ++++++++ src/machine.h | 12 +++++++++++ src/posix.cpp | 10 +++++++++ src/system.h | 1 + src/windows.cpp | 10 +++++++++ test/Threads.java | 37 ++++++++++++++++++++++++++------- 7 files changed, 81 insertions(+), 10 deletions(-) diff --git a/classpath/java/lang/Thread.java b/classpath/java/lang/Thread.java index 2d321193a0..4846287daf 100644 --- a/classpath/java/lang/Thread.java +++ b/classpath/java/lang/Thread.java @@ -139,9 +139,17 @@ public class Thread implements Runnable { public static native Thread currentThread(); - public native void interrupt(); + public void interrupt() { + interrupt(peer); + } - public native boolean interrupted(); + private static native boolean interrupt(long peer); + + public boolean interrupted() { + return interrupted(peer); + } + + private static native boolean interrupted(long peer); public static boolean isInterrupted() { return currentThread().interrupted; diff --git a/src/classpath-avian.cpp b/src/classpath-avian.cpp index 89c6cdc13d..7d3459307b 100644 --- a/src/classpath-avian.cpp +++ b/src/classpath-avian.cpp @@ -554,6 +554,15 @@ Avian_java_lang_Thread_interrupt interrupt(t, reinterpret_cast(peer)); } +extern "C" JNIEXPORT int64_t JNICALL +Avian_java_lang_Thread_interrupted +(Thread* t, object, uintptr_t* arguments) +{ + int64_t peer; memcpy(&peer, arguments, 8); + + return getAndClearInterrupted(t, reinterpret_cast(peer)); +} + extern "C" JNIEXPORT int64_t JNICALL Avian_java_lang_Thread_getStackTrace (Thread* t, object, uintptr_t* arguments) diff --git a/src/machine.h b/src/machine.h index 2a69b8e038..5ed0bb104c 100644 --- a/src/machine.h +++ b/src/machine.h @@ -3236,6 +3236,18 @@ interrupt(Thread* t, Thread* target) } } +inline bool +getAndClearInterrupted(Thread* t, Thread* target) +{ + if (acquireSystem(t, target)) { + bool result = target->systemThread->getAndClearInterrupted(); + releaseSystem(t, target); + return result; + } else { + return false; + } +} + object intern(Thread* t, object s); diff --git a/src/posix.cpp b/src/posix.cpp index 2a5c7daf78..59d2fb08f0 100644 --- a/src/posix.cpp +++ b/src/posix.cpp @@ -151,6 +151,16 @@ class MySystem: public System { expect(s, rv == 0); } + virtual bool getAndClearInterrupted() { + ACQUIRE(mutex); + + bool interrupted = r->interrupted(); + + r->setInterrupted(false); + + return interrupted; + } + virtual void join() { int rv UNUSED = pthread_join(thread, 0); expect(s, rv == 0); diff --git a/src/system.h b/src/system.h index 6e2ea14721..25ec0bd455 100644 --- a/src/system.h +++ b/src/system.h @@ -29,6 +29,7 @@ class System { class Thread { public: virtual void interrupt() = 0; + virtual bool getAndClearInterrupted() = 0; virtual void join() = 0; virtual void dispose() = 0; }; diff --git a/src/windows.cpp b/src/windows.cpp index 4c75b80870..75591d614c 100644 --- a/src/windows.cpp +++ b/src/windows.cpp @@ -100,6 +100,16 @@ class MySystem: public System { } } + virtual bool getAndClearInterrupted() { + ACQUIRE(s, mutex); + + bool interrupted = r->interrupted(); + + r->setInterrupted(false); + + return interrupted; + } + virtual void join() { int r UNUSED = WaitForSingleObject(thread, INFINITE); assert(s, r == WAIT_OBJECT_0); diff --git a/test/Threads.java b/test/Threads.java index c411c8c61c..a6292e74fb 100644 --- a/test/Threads.java +++ b/test/Threads.java @@ -1,15 +1,36 @@ public class Threads implements Runnable { public static void main(String[] args) { - Threads test = new Threads(); - Thread thread = new Thread(test); + { Threads test = new Threads(); + Thread thread = new Thread(test); - try { - synchronized (test) { - thread.start(); - test.wait(); + try { + synchronized (test) { + thread.start(); + test.wait(); + } + } catch (Throwable e) { + e.printStackTrace(); } - } catch (Throwable e) { - e.printStackTrace(); + } + + { Thread thread = new Thread() { + public void run() { + while (true) { + System.out.print("."); + try { + sleep(1000); + } catch (Exception e) { + System.out.println("thread interrupted? " + interrupted()); + break; + } + } + } + }; + thread.start(); + + System.out.println("\nAbout to interrupt..."); + thread.interrupt(); + System.out.println("\nInterrupted!"); } System.out.println("finished"); From 6ea444f6bf2a1a89d670be74c6337ba91719d97b Mon Sep 17 00:00:00 2001 From: Seth Goings Date: Thu, 1 Sep 2011 10:17:21 -0600 Subject: [PATCH 03/50] Fixed incorrect handling of \r\n --- classpath/java/io/BufferedReader.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/classpath/java/io/BufferedReader.java b/classpath/java/io/BufferedReader.java index 236a086b80..2618dfdbd6 100644 --- a/classpath/java/io/BufferedReader.java +++ b/classpath/java/io/BufferedReader.java @@ -46,8 +46,7 @@ public class BufferedReader extends Reader { sb.append(buffer, position, i - position); position = i + 1; if(i+1 < limit && buffer[i+1] == '\n') { - position = i + 1; - return sb.toString(); + position = i + 2; } return sb.toString(); } else if (buffer[i] == '\n') { From fce05f680ad9c133d2d2f9f652b24bf98fe9375b Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Mon, 12 Sep 2011 13:45:21 -0600 Subject: [PATCH 04/50] only generate crash dumps for access violations and divides-by-zero Some apps and libraries may generate recoverable SEH exceptions on Windows, in which cases we don't want to waste time and disk space generating memory dumps. --- src/windows.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/windows.cpp b/src/windows.cpp index d0839033dc..375baee201 100644 --- a/src/windows.cpp +++ b/src/windows.cpp @@ -974,13 +974,11 @@ handleException(LPEXCEPTION_POINTERS e) if (jump) { return EXCEPTION_CONTINUE_EXECUTION; + } else if (system->crashDumpDirectory) { + dump(e, system->crashDumpDirectory); } } - if (system->crashDumpDirectory) { - dump(e, system->crashDumpDirectory); - } - return EXCEPTION_CONTINUE_SEARCH; } From 17a376c966568397b30ddf80fcf9494ec0b3cdc4 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Mon, 12 Sep 2011 13:45:21 -0600 Subject: [PATCH 05/50] only generate crash dumps for access violations and divides-by-zero Some apps and libraries may generate recoverable SEH exceptions on Windows, in which cases we don't want to waste time and disk space generating memory dumps. --- src/windows.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/windows.cpp b/src/windows.cpp index d0839033dc..375baee201 100644 --- a/src/windows.cpp +++ b/src/windows.cpp @@ -974,13 +974,11 @@ handleException(LPEXCEPTION_POINTERS e) if (jump) { return EXCEPTION_CONTINUE_EXECUTION; + } else if (system->crashDumpDirectory) { + dump(e, system->crashDumpDirectory); } } - if (system->crashDumpDirectory) { - dump(e, system->crashDumpDirectory); - } - return EXCEPTION_CONTINUE_SEARCH; } From 61457dca701dae80ac07977b702942af4f71fbdc Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Thu, 29 Sep 2011 18:23:15 -0600 Subject: [PATCH 06/50] add workaround for buffer overflow on iOS Simulator It seems that the Apple iOS Simulator's stat implementation writes beyond the end of the struct stat we pass it, which can clobber unrelated parts of the stack. Perhaps this is due to some kind of header/library mismatch, but I've been unable to track it down so far. The workaround is to give it 8 words more than it should need, where 8 is a number I just made up and seems to work. --- src/posix.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/posix.cpp b/src/posix.cpp index f1a8ca0065..513d8c8fb7 100644 --- a/src/posix.cpp +++ b/src/posix.cpp @@ -772,13 +772,22 @@ class MySystem: public System { } virtual FileType stat(const char* name, unsigned* length) { - struct stat s; - int r = ::stat(name, &s); + // Ugly Hack Alert: It seems that the Apple iOS Simulator's stat + // implementation writes beyond the end of the struct stat we pass + // it, which can clobber unrelated parts of the stack. Perhaps + // this is due to some kind of header/library mismatch, but I've + // been unable to track it down so far. The workaround is to give + // it 8 words more than it should need, where 8 is a number I just + // made up and seems to work. + void* array[ceiling(sizeof(struct stat), sizeof(void*)) + 8]; + struct stat* s = reinterpret_cast(array); + + int r = ::stat(name, s); if (r == 0) { - if (S_ISREG(s.st_mode)) { - *length = s.st_size; + if (S_ISREG(s->st_mode)) { + *length = s->st_size; return TypeFile; - } else if (S_ISDIR(s.st_mode)) { + } else if (S_ISDIR(s->st_mode)) { *length = 0; return TypeDirectory; } else { From 84bcbbcaa35521f67876a548d1799b167ce00a64 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Thu, 29 Sep 2011 18:25:03 -0600 Subject: [PATCH 07/50] implement asReadOnlyBuffer and getShort(int) in ByteBuffer --- classpath/java/nio/ByteBuffer.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/classpath/java/nio/ByteBuffer.java b/classpath/java/nio/ByteBuffer.java index e8191f90a3..2dee03f7d9 100644 --- a/classpath/java/nio/ByteBuffer.java +++ b/classpath/java/nio/ByteBuffer.java @@ -36,6 +36,13 @@ public class ByteBuffer extends Buffer implements Comparable { position = 0; } + public ByteBuffer asReadOnlyBuffer() { + ByteBuffer b = new ByteBuffer(array, arrayOffset, capacity, true); + b.position(position()); + b.limit(limit()); + return b; + } + public int compareTo(ByteBuffer o) { int end = (remaining() < o.remaining() ? remaining() : o.remaining()); @@ -159,6 +166,13 @@ public class ByteBuffer extends Buffer implements Comparable { | ((array[p + 3] & 0xFF)); } + public short getShort(int position) { + checkGet(position, 2); + + int p = arrayOffset + position; + return (short) (((array[p] & 0xFF) << 8) | ((array[p + 1] & 0xFF))); + } + public int getInt() { checkGet(4); int i = get() << 24; From a8bb0d074be5756f6d8899fc084f77d6f71770d5 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Thu, 29 Sep 2011 18:25:41 -0600 Subject: [PATCH 08/50] implement first() and last() in TreeSet --- classpath/java/util/TreeSet.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/classpath/java/util/TreeSet.java b/classpath/java/util/TreeSet.java index 16f291a134..01e76496a2 100644 --- a/classpath/java/util/TreeSet.java +++ b/classpath/java/util/TreeSet.java @@ -41,6 +41,18 @@ public class TreeSet extends AbstractSet implements Collection { add(item); } } + + public T first() { + if (isEmpty()) throw new NoSuchElementException(); + + return set.first().value().value; + } + + public T last() { + if (isEmpty()) throw new NoSuchElementException(); + + return set.last().value().value; + } public Iterator iterator() { return new MyIterator(set.first()); From 296cb7484761a05f7d97841aca0f9ef725cb8076 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Thu, 29 Sep 2011 18:26:50 -0600 Subject: [PATCH 09/50] add bare-bones ConcurrentLinkedQueue implementation --- classpath/avian/Atomic.java | 10 +++ .../concurrent/ConcurrentLinkedQueue.java | 88 +++++++++++++++++++ src/classpath-avian.cpp | 27 ++++++ 3 files changed, 125 insertions(+) create mode 100644 classpath/avian/Atomic.java create mode 100644 classpath/java/util/concurrent/ConcurrentLinkedQueue.java diff --git a/classpath/avian/Atomic.java b/classpath/avian/Atomic.java new file mode 100644 index 0000000000..1d6ee7569e --- /dev/null +++ b/classpath/avian/Atomic.java @@ -0,0 +1,10 @@ +package avian; + +import java.lang.reflect.Field; + +public class Atomic { + public static native long getOffset(Field field); + + public static native boolean compareAndSwapObject + (Object o, long offset, Object old, Object new_); +} diff --git a/classpath/java/util/concurrent/ConcurrentLinkedQueue.java b/classpath/java/util/concurrent/ConcurrentLinkedQueue.java new file mode 100644 index 0000000000..aa920311ca --- /dev/null +++ b/classpath/java/util/concurrent/ConcurrentLinkedQueue.java @@ -0,0 +1,88 @@ +package java.util.concurrent; + +import avian.Atomic; + +public class ConcurrentLinkedQueue { + private static final long QueueHead; + private static final long QueueTail; + private static final long NodeNext; + + static { + try { + QueueHead = Atomic.getOffset + (ConcurrentLinkedQueue.class.getField("head")); + + QueueTail = Atomic.getOffset + (ConcurrentLinkedQueue.class.getField("tail")); + + NodeNext = Atomic.getOffset + (Node.class.getField("next")); + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } + } + + private volatile Node head = new Node(null, null); + private volatile Node tail = head; + + public boolean add(T value) { + Node n = new Node(value, null); + while (true) { + Node t = tail; + Node next = tail.next; + if (t == tail) { + if (next != null) { + Atomic.compareAndSwapObject(this, QueueTail, t, next); + } else if (Atomic.compareAndSwapObject(tail, NodeNext, null, n)) { + Atomic.compareAndSwapObject(this, QueueTail, t, n); + break; + } + } + } + + return true; + } + + public T peek() { + return poll(false); + } + + public T poll() { + return poll(true); + } + + public T poll(boolean remove) { + while (true) { + Node h = head; + Node t = tail; + Node next = head.next; + + if (h == head) { + if (h == t) { + if (next != null) { + Atomic.compareAndSwapObject(this, QueueTail, t, next); + } else { + return null; + } + } else { + T value = next.value; + if ((! remove) + || Atomic.compareAndSwapObject(this, QueueHead, h, next)) + { + return value; + } + } + } + } + } + + private static class Node { + public volatile T value; + public volatile Node next; + + public Node(T value, Node next) { + this.value = value; + this.next = next; + } + } +} diff --git a/src/classpath-avian.cpp b/src/classpath-avian.cpp index 7d806ce87e..39cde4ff83 100644 --- a/src/classpath-avian.cpp +++ b/src/classpath-avian.cpp @@ -605,6 +605,33 @@ Avian_java_lang_Thread_yield t->m->system->yield(); } +extern "C" JNIEXPORT int64_t JNICALL +Avian_avian_Atomic_getOffset +(Thread* t, object, uintptr_t* arguments) +{ + return fieldOffset + (t, jfieldVmField(t, reinterpret_cast(arguments[0]))); +} + +extern "C" JNIEXPORT int64_t JNICALL +Avian_avian_Atomic_compareAndSwapObject +(Thread* t, object, uintptr_t* arguments) +{ + object target = reinterpret_cast(arguments[0]); + int64_t offset; memcpy(&offset, arguments + 1, 8); + uintptr_t expect = arguments[3]; + uintptr_t update = arguments[4]; + + bool success = atomicCompareAndSwap + (&cast(target, offset), expect, update); + + if (success) { + mark(t, target, offset); + } + + return success; +} + extern "C" JNIEXPORT int64_t JNICALL Avian_avian_Classes_primitiveClass (Thread* t, object, uintptr_t* arguments) From b862f5f90acef7eeadd9350ba5501a8cb32b79c9 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Thu, 29 Sep 2011 18:27:34 -0600 Subject: [PATCH 10/50] add bare-bones UUID implementation --- classpath/java/util/UUID.java | 45 +++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 classpath/java/util/UUID.java diff --git a/classpath/java/util/UUID.java b/classpath/java/util/UUID.java new file mode 100644 index 0000000000..54f5e6295e --- /dev/null +++ b/classpath/java/util/UUID.java @@ -0,0 +1,45 @@ +package java.util; + +public class UUID { + private final byte[] data; + + private UUID(byte[] data) { + this.data = data; + } + + public static UUID randomUUID() { + byte[] array = new byte[16]; + + new Random().nextBytes(array); + + array[6] &= 0x0f; + array[6] |= 0x40; + array[8] &= 0x3f; + array[8] |= 0x80; + + return new UUID(array); + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + toHex(sb, data, 0, 4); sb.append('-'); + toHex(sb, data, 4, 2); sb.append('-'); + toHex(sb, data, 6, 2); sb.append('-'); + toHex(sb, data, 8, 2); sb.append('-'); + toHex(sb, data, 10, 6); + return sb.toString(); + } + + private static char toHex(int i) { + return (char) (i < 10 ? i + '0' : (i - 10) + 'A'); + } + + private static void toHex(StringBuilder sb, byte[] array, int offset, + int length) + { + for (int i = offset; i < offset + length; ++i) { + sb.append(toHex((array[i] >> 4) & 0xf)); + sb.append(toHex((array[i] ) & 0xf)); + } + } +} From 88d614eb256987d7a3fe4a6c39f740bd681c2d95 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Fri, 30 Sep 2011 13:17:28 -0600 Subject: [PATCH 11/50] remove distinction between thunks and bootThunks in compile.cpp Now that the AOT-compiled code image is position-independent, there is no further need for this distinction. In fact, it was harmful, because we were still using runtime-generated thunks when we should have been using the ones in the code image. This resulted in EXC_BAD_ACCESS errors on non-jailbroken iOS devices. --- src/compile.cpp | 43 ++++++++++++------------------------------- 1 file changed, 12 insertions(+), 31 deletions(-) diff --git a/src/compile.cpp b/src/compile.cpp index c993e82f6e..eb44c70972 100644 --- a/src/compile.cpp +++ b/src/compile.cpp @@ -2255,9 +2255,6 @@ defaultThunk(MyThread* t); uintptr_t nativeThunk(MyThread* t); -uintptr_t -bootNativeThunk(MyThread* t); - uintptr_t aioobThunk(MyThread* t); @@ -8816,9 +8813,9 @@ class MyProcessor: public Processor { root(t, MethodTreeSentinal)); set(t, root(t, MethodTree), TreeNodeRight, root(t, MethodTreeSentinal)); - } - local::compileThunks(static_cast(t), &codeAllocator); + local::compileThunks(static_cast(t), &codeAllocator); + } segFaultHandler.m = t->m; expect(t, t->m->system->success @@ -8888,7 +8885,6 @@ class MyProcessor: public Processor { SignalHandler divideByZeroHandler; FixedAllocator codeAllocator; ThunkCollection thunks; - ThunkCollection bootThunks; unsigned callTableSize; bool useNativeFeatures; void* thunkTable[dummyIndex + 1]; @@ -8913,7 +8909,7 @@ compileMethod2(MyThread* t, void* ip) if ((methodFlags(t, target) & ACC_NATIVE) and useLongJump(t, reinterpret_cast(ip))) { - address = bootNativeThunk(t); + address = nativeThunk(t); } else { address = methodAddress(t, target); } @@ -8960,7 +8956,7 @@ isThunk(MyThread* t, void* ip) { MyProcessor* p = processor(t); - return isThunk(&(p->thunks), ip) or isThunk(&(p->bootThunks), ip); + return isThunk(&(p->thunks), ip); } bool @@ -9024,9 +9020,7 @@ isThunkUnsafeStack(MyThread* t, void* ip) { MyProcessor* p = processor(t); - return isThunk(t, ip) - and (isThunkUnsafeStack(&(p->thunks), ip) - or isThunkUnsafeStack(&(p->bootThunks), ip)); + return isThunk(t, ip) and isThunkUnsafeStack(&(p->thunks), ip); } object @@ -9274,14 +9268,14 @@ findThunks(MyThread* t, BootImage* image, uint8_t* code) { MyProcessor* p = processor(t); - p->bootThunks.default_ = thunkToThunk(image->thunks.default_, code); - p->bootThunks.defaultVirtual + p->thunks.default_ = thunkToThunk(image->thunks.default_, code); + p->thunks.defaultVirtual = thunkToThunk(image->thunks.defaultVirtual, code); - p->bootThunks.native = thunkToThunk(image->thunks.native, code); - p->bootThunks.aioob = thunkToThunk(image->thunks.aioob, code); - p->bootThunks.stackOverflow + p->thunks.native = thunkToThunk(image->thunks.native, code); + p->thunks.aioob = thunkToThunk(image->thunks.aioob, code); + p->thunks.stackOverflow = thunkToThunk(image->thunks.stackOverflow, code); - p->bootThunks.table = thunkToThunk(image->thunks.table, code); + p->thunks.table = thunkToThunk(image->thunks.table, code); } void @@ -9622,12 +9616,6 @@ defaultThunk(MyThread* t) return reinterpret_cast(processor(t)->thunks.default_.start); } -uintptr_t -bootDefaultThunk(MyThread* t) -{ - return reinterpret_cast(processor(t)->bootThunks.default_.start); -} - uintptr_t defaultVirtualThunk(MyThread* t) { @@ -9641,12 +9629,6 @@ nativeThunk(MyThread* t) return reinterpret_cast(processor(t)->thunks.native.start); } -uintptr_t -bootNativeThunk(MyThread* t) -{ - return reinterpret_cast(processor(t)->bootThunks.native.start); -} - uintptr_t aioobThunk(MyThread* t) { @@ -9662,8 +9644,7 @@ stackOverflowThunk(MyThread* t) bool unresolved(MyThread* t, uintptr_t methodAddress) { - return methodAddress == defaultThunk(t) - or methodAddress == bootDefaultThunk(t); + return methodAddress == defaultThunk(t); } uintptr_t From b063f10ad5a4db95684b33166557537c94c78650 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Fri, 30 Sep 2011 14:44:25 -0600 Subject: [PATCH 12/50] fix Windows build --- makefile | 1 + src/compile.cpp | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/makefile b/makefile index 44cb1fed48..651e97625f 100755 --- a/makefile +++ b/makefile @@ -384,6 +384,7 @@ ifeq ($(platform),windows) strip = x86_64-w64-mingw32-strip --strip-all else build-system = windows + cflags += -DTARGET_PLATFORM_WINDOWS common-cflags += "-I$(JAVA_HOME)/include/win32" build-cflags = $(common-cflags) -I$(src) -I$(inc) -mthreads openjdk-extra-cflags = diff --git a/src/compile.cpp b/src/compile.cpp index eb44c70972..1c3855ab8f 100644 --- a/src/compile.cpp +++ b/src/compile.cpp @@ -8392,6 +8392,12 @@ class MyProcessor: public Processor { difference(&(t->virtualCallTarget), t)); fprintf(stderr, "virtualCallIndex %d\n", difference(&(t->virtualCallIndex), t)); + fprintf(stderr, "heapImage %d\n", + difference(&(t->heapImage), t)); + fprintf(stderr, "codeImage %d\n", + difference(&(t->codeImage), t)); + fprintf(stderr, "thunkTable %d\n", + difference(&(t->thunkTable), t)); exit(0); } From df4626c26090409f51215de419508f1235b0b49c Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Fri, 30 Sep 2011 15:21:12 -0600 Subject: [PATCH 13/50] fix Windows cross build --- makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/makefile b/makefile index 651e97625f..14fc99e4d7 100755 --- a/makefile +++ b/makefile @@ -372,7 +372,8 @@ ifeq ($(platform),windows) exe-suffix = .exe lflags = -L$(lib) $(common-lflags) -lws2_32 -mwindows -mconsole - cflags = -I$(inc) $(common-cflags) -DWINVER=0x0500 + cflags = -I$(inc) $(common-cflags) -DWINVER=0x0500 -DTARGET_PLATFORM_WINDOWS + ifeq (,$(filter mingw32 cygwin,$(build-platform))) openjdk-extra-cflags += -I$(src)/openjdk/caseSensitive @@ -384,7 +385,6 @@ ifeq ($(platform),windows) strip = x86_64-w64-mingw32-strip --strip-all else build-system = windows - cflags += -DTARGET_PLATFORM_WINDOWS common-cflags += "-I$(JAVA_HOME)/include/win32" build-cflags = $(common-cflags) -I$(src) -I$(inc) -mthreads openjdk-extra-cflags = From 5f05110f336e8e16a078ddf2cc883fce91bf46e6 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Fri, 30 Sep 2011 17:00:45 -0600 Subject: [PATCH 14/50] fix type of TargetPointerMask --- src/target.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/target.h b/src/target.h index bbd39238d1..509a574996 100644 --- a/src/target.h +++ b/src/target.h @@ -147,7 +147,7 @@ const unsigned TargetFieldOffset = 8; const unsigned TargetBitsPerWord = TargetBytesPerWord * 8; -const uintptr_t TargetPointerMask +const target_uintptr_t TargetPointerMask = ((~static_cast(0)) / TargetBytesPerWord) * TargetBytesPerWord; From ed6945dec7fe217287a616870aa636a81ed3c6c5 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Fri, 30 Sep 2011 18:25:44 -0600 Subject: [PATCH 15/50] fix offset calculations in arm.cpp and powerpc.cpp --- src/arm.cpp | 5 +++-- src/powerpc.cpp | 7 ++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/arm.cpp b/src/arm.cpp index 2c7549238c..af1c6398a2 100644 --- a/src/arm.cpp +++ b/src/arm.cpp @@ -2413,12 +2413,13 @@ class MyAssembler: public Assembler { poolSize += TargetBytesPerWord; } - if (needJump(b)) { + bool jump = needJump(b); + if (jump) { write4 (dst + dstOffset, ::b((poolSize + TargetBytesPerWord - 8) >> 2)); } - dstOffset += poolSize + TargetBytesPerWord; + dstOffset += poolSize + (jump ? TargetBytesPerWord : 0); } unsigned size = b->size - blockOffset; diff --git a/src/powerpc.cpp b/src/powerpc.cpp index 59e0c77146..7e315b1f35 100644 --- a/src/powerpc.cpp +++ b/src/powerpc.cpp @@ -2733,11 +2733,12 @@ class MyAssembler: public Assembler { assert(&c, jumpTableSize); - if (needJump(b)) { + bool jump = needJump(b); + if (jump) { write4(dst + dstOffset, ::b(jumpTableSize + TargetBytesPerWord)); } - dstOffset += jumpTableSize + TargetBytesPerWord; + dstOffset += jumpTableSize + (jump ? TargetBytesPerWord : 0); } unsigned size = b->size - blockOffset; @@ -2749,7 +2750,7 @@ class MyAssembler: public Assembler { dstOffset += size; } - unsigned index = c.code.length(); + unsigned index = dstOffset; assert(&c, index % TargetBytesPerWord == 0); for (ConstantPoolEntry* e = c.constantPool; e; e = e->next) { e->address = dst + index; From ad22de6d737c306aad8371e92407339a5d6c7ae7 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Fri, 30 Sep 2011 18:40:03 -0600 Subject: [PATCH 16/50] fix uninitialized variable in bootimage.cpp --- src/bootimage.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/bootimage.cpp b/src/bootimage.cpp index 05f99c605e..605000d339 100644 --- a/src/bootimage.cpp +++ b/src/bootimage.cpp @@ -211,10 +211,12 @@ allFields(Thread* t, object typeMaps, object c, unsigned* count, object* array) includeMembers = false; *count += reinterpret_cast(&byteArrayBody(t, *array, 0)) ->fixedFieldCount; - } else if (classSuper(t, c)) { + } else { includeMembers = true; - fields = getNonStaticFields - (t, typeMaps, classSuper(t, c), fields, count, array); + if (classSuper(t, c)) { + fields = getNonStaticFields + (t, typeMaps, classSuper(t, c), fields, count, array); + } } if (classFieldTable(t, c)) { From d89a96ae6de1d4fd440a845eff2093aaabd6b78d Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Fri, 30 Sep 2011 18:40:43 -0600 Subject: [PATCH 17/50] use absolute path for test-executable in makefile This is necessary to avoid crashes on OS X due to CFBundleGetMainBundle and friends not behaving well otherwise. --- makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/makefile b/makefile index 14fc99e4d7..1e7946dc24 100755 --- a/makefile +++ b/makefile @@ -54,7 +54,7 @@ test = test classpath = avian -test-executable = $(executable) +test-executable = $(shell pwd)/$(executable) boot-classpath = $(classpath-build) embed-prefix = /avian-embedded @@ -110,7 +110,7 @@ ifneq ($(openjdk),) boot-javahome-object = $(build)/boot-javahome.o else options := $(options)-openjdk - test-executable = $(executable-dynamic) + test-executable = $(shell pwd)/$(executable-dynamic) library-path = \ $(library-path-variable)=$(build):$(openjdk)/jre/lib/$(openjdk-arch) javahome = "$$($(native-path) "$(openjdk)/jre")" From 043e4669214d1c0cb6cfd090a0499d7b1ec5c31c Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Fri, 30 Sep 2011 18:44:13 -0600 Subject: [PATCH 18/50] change bool field types to uint8_t in Fixie class The bool type is 32 bits on Darwin/PowerPC, unlike other platforms. To make bootimage creation consistent, we now use an explicit 8-bit type. --- src/heap.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/heap.cpp b/src/heap.cpp index c8f30c0369..e441640364 100644 --- a/src/heap.cpp +++ b/src/heap.cpp @@ -544,9 +544,9 @@ class Fixie { // you add/remove/change fields in this class: uint8_t age; - bool hasMask; - bool marked; - bool dirty; + uint8_t hasMask; + uint8_t marked; + uint8_t dirty; uint32_t size; Fixie* next; Fixie** handle; From db9f9a300d3e9fcef2124120a6c7cb37444b9edd Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Sat, 1 Oct 2011 18:11:02 -0600 Subject: [PATCH 19/50] Revert "remove distinction between thunks and bootThunks in compile.cpp" This reverts commit 88d614eb256987d7a3fe4a6c39f740bd681c2d95. It turns out we still need separate sets of thunks for AOT-compiled and JIT-compiled code to ensure we can always generate efficient jumps and calls to thunks on architectures such as ARM and PowerPC, whose relative jumps and calls have limited ranges. --- src/compile.cpp | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/src/compile.cpp b/src/compile.cpp index 1c3855ab8f..b1ae67a4b8 100644 --- a/src/compile.cpp +++ b/src/compile.cpp @@ -2255,6 +2255,9 @@ defaultThunk(MyThread* t); uintptr_t nativeThunk(MyThread* t); +uintptr_t +bootNativeThunk(MyThread* t); + uintptr_t aioobThunk(MyThread* t); @@ -8819,10 +8822,10 @@ class MyProcessor: public Processor { root(t, MethodTreeSentinal)); set(t, root(t, MethodTree), TreeNodeRight, root(t, MethodTreeSentinal)); - - local::compileThunks(static_cast(t), &codeAllocator); } + local::compileThunks(static_cast(t), &codeAllocator); + segFaultHandler.m = t->m; expect(t, t->m->system->success (t->m->system->handleSegFault(&segFaultHandler))); @@ -8891,6 +8894,7 @@ class MyProcessor: public Processor { SignalHandler divideByZeroHandler; FixedAllocator codeAllocator; ThunkCollection thunks; + ThunkCollection bootThunks; unsigned callTableSize; bool useNativeFeatures; void* thunkTable[dummyIndex + 1]; @@ -8915,7 +8919,7 @@ compileMethod2(MyThread* t, void* ip) if ((methodFlags(t, target) & ACC_NATIVE) and useLongJump(t, reinterpret_cast(ip))) { - address = nativeThunk(t); + address = bootNativeThunk(t); } else { address = methodAddress(t, target); } @@ -8962,7 +8966,7 @@ isThunk(MyThread* t, void* ip) { MyProcessor* p = processor(t); - return isThunk(&(p->thunks), ip); + return isThunk(&(p->thunks), ip) or isThunk(&(p->bootThunks), ip); } bool @@ -9026,7 +9030,9 @@ isThunkUnsafeStack(MyThread* t, void* ip) { MyProcessor* p = processor(t); - return isThunk(t, ip) and isThunkUnsafeStack(&(p->thunks), ip); + return isThunk(t, ip) + and (isThunkUnsafeStack(&(p->thunks), ip) + or isThunkUnsafeStack(&(p->bootThunks), ip)); } object @@ -9274,14 +9280,14 @@ findThunks(MyThread* t, BootImage* image, uint8_t* code) { MyProcessor* p = processor(t); - p->thunks.default_ = thunkToThunk(image->thunks.default_, code); - p->thunks.defaultVirtual + p->bootThunks.default_ = thunkToThunk(image->thunks.default_, code); + p->bootThunks.defaultVirtual = thunkToThunk(image->thunks.defaultVirtual, code); - p->thunks.native = thunkToThunk(image->thunks.native, code); - p->thunks.aioob = thunkToThunk(image->thunks.aioob, code); - p->thunks.stackOverflow + p->bootThunks.native = thunkToThunk(image->thunks.native, code); + p->bootThunks.aioob = thunkToThunk(image->thunks.aioob, code); + p->bootThunks.stackOverflow = thunkToThunk(image->thunks.stackOverflow, code); - p->thunks.table = thunkToThunk(image->thunks.table, code); + p->bootThunks.table = thunkToThunk(image->thunks.table, code); } void @@ -9622,6 +9628,12 @@ defaultThunk(MyThread* t) return reinterpret_cast(processor(t)->thunks.default_.start); } +uintptr_t +bootDefaultThunk(MyThread* t) +{ + return reinterpret_cast(processor(t)->bootThunks.default_.start); +} + uintptr_t defaultVirtualThunk(MyThread* t) { @@ -9635,6 +9647,12 @@ nativeThunk(MyThread* t) return reinterpret_cast(processor(t)->thunks.native.start); } +uintptr_t +bootNativeThunk(MyThread* t) +{ + return reinterpret_cast(processor(t)->bootThunks.native.start); +} + uintptr_t aioobThunk(MyThread* t) { @@ -9650,7 +9668,8 @@ stackOverflowThunk(MyThread* t) bool unresolved(MyThread* t, uintptr_t methodAddress) { - return methodAddress == defaultThunk(t); + return methodAddress == defaultThunk(t) + or methodAddress == bootDefaultThunk(t); } uintptr_t From 248ff26581ee0a0a72d5eeec7323c50eb7e727d7 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Mon, 3 Oct 2011 08:04:58 -0600 Subject: [PATCH 20/50] fix thinko in machine.cpp --- src/machine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/machine.cpp b/src/machine.cpp index 838564b5ef..bdfdef42a3 100644 --- a/src/machine.cpp +++ b/src/machine.cpp @@ -2566,7 +2566,7 @@ Thread::init() enter(this, ActiveState); - if (image and image) { + if (image and code) { m->processor->boot(this, image, code); } else { boot(this); From 5c39819cf0410dd555052f9fbc020843840f9a29 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Mon, 3 Oct 2011 08:05:25 -0600 Subject: [PATCH 21/50] fix uninitialized value warnings in bootimage.cpp --- src/bootimage.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bootimage.cpp b/src/bootimage.cpp index 605000d339..28f45dd382 100644 --- a/src/bootimage.cpp +++ b/src/bootimage.cpp @@ -1316,9 +1316,9 @@ writeBootImage2(Thread* t, FILE* bootimageOutput, FILE* codeOutput, unsigned buildOffset = BytesPerWord; unsigned targetOffset = TargetBytesPerWord; bool sawArray = false; - Type type; - unsigned buildSize; - unsigned targetSize; + Type type = Type_none; + unsigned buildSize = 0; + unsigned targetSize = 0; for (unsigned j = 1; j < count; ++j) { switch (source[j - 1]) { case Type_object: From 8501ce871173293a22bdf75ec46218da18f311c5 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Mon, 3 Oct 2011 08:05:49 -0600 Subject: [PATCH 22/50] avoid using runtime-generated thunks for bootimage native methods --- src/compile.cpp | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/compile.cpp b/src/compile.cpp index b1ae67a4b8..4d7d7cecdd 100644 --- a/src/compile.cpp +++ b/src/compile.cpp @@ -2274,7 +2274,7 @@ uintptr_t methodAddress(Thread* t, object method) { if (methodFlags(t, method) & ACC_NATIVE) { - return nativeThunk(static_cast(t)); + return bootNativeThunk(static_cast(t)); } else { return methodCompiled(t, method); } @@ -8826,6 +8826,10 @@ class MyProcessor: public Processor { local::compileThunks(static_cast(t), &codeAllocator); + if (not (image and code)) { + bootThunks = thunks; + } + segFaultHandler.m = t->m; expect(t, t->m->system->success (t->m->system->handleSegFault(&segFaultHandler))); @@ -8915,20 +8919,22 @@ compileMethod2(MyThread* t, void* ip) compile(t, codeAllocator(t), 0, target); - uintptr_t address; - if ((methodFlags(t, target) & ACC_NATIVE) - and useLongJump(t, reinterpret_cast(ip))) - { - address = bootNativeThunk(t); - } else { - address = methodAddress(t, target); - } - uint8_t* updateIp = static_cast(ip); MyProcessor* p = processor(t); - if (updateIp < p->codeImage or updateIp >= p->codeImage + p->codeImageSize) { + bool updateCaller = updateIp < p->codeImage + or updateIp >= p->codeImage + p->codeImageSize; + + uintptr_t address; + if (methodFlags(t, target) & ACC_NATIVE) { + address = useLongJump(t, reinterpret_cast(ip)) + or (not updateCaller) ? bootNativeThunk(t) : nativeThunk(t); + } else { + address = methodAddress(t, target); + } + + if (updateCaller) { UnaryOperation op; if (callNodeFlags(t, node) & TraceElement::LongCall) { if (callNodeFlags(t, node) & TraceElement::TailCall) { From 39bee886e3e96977fc8098d91f9e348dc0954efc Mon Sep 17 00:00:00 2001 From: JET Date: Thu, 20 Oct 2011 13:45:47 -0600 Subject: [PATCH 23/50] ByteBuffer and InputStream now better match the standard. --- classpath/java/io/InputStream.java | 2 +- classpath/java/nio/Buffer.java | 23 ++++++++++++++--------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/classpath/java/io/InputStream.java b/classpath/java/io/InputStream.java index a225432044..8e74c2db8d 100644 --- a/classpath/java/io/InputStream.java +++ b/classpath/java/io/InputStream.java @@ -56,7 +56,7 @@ public abstract class InputStream { } public void reset() throws IOException { - // ignore + throw new IOException("mark/reset not supported"); } public boolean markSupported() { diff --git a/classpath/java/nio/Buffer.java b/classpath/java/nio/Buffer.java index 3de26da72f..c93c11f347 100644 --- a/classpath/java/nio/Buffer.java +++ b/classpath/java/nio/Buffer.java @@ -15,45 +15,50 @@ public abstract class Buffer { protected int position; protected int limit; - public int limit() { + public final int limit() { return limit; } - public int remaining() { + public final int remaining() { return limit-position; } - public int position() { + public final int position() { return position; } - public int capacity() { + public final int capacity() { return capacity; } - public Buffer limit(int newLimit) { + public final Buffer limit(int newLimit) { limit = newLimit; return this; } - public Buffer position(int newPosition) { + public final Buffer position(int newPosition) { position = newPosition; return this; } - public boolean hasRemaining() { + public final boolean hasRemaining() { return remaining() > 0; } - public Buffer clear() { + public final Buffer clear() { position = 0; limit = capacity; return this; } - public Buffer flip() { + public final Buffer flip() { limit = position; position = 0; return this; } + + public final Buffer rewind() { + position = 0; + return this; + } } From 3b1c769d2b6ba67fe47cdf583ed186944bb595a9 Mon Sep 17 00:00:00 2001 From: JET Date: Mon, 24 Oct 2011 09:01:17 -0600 Subject: [PATCH 24/50] Fixed RandomAccessFile to update position after reads. --- classpath/java/io/RandomAccessFile.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/classpath/java/io/RandomAccessFile.java b/classpath/java/io/RandomAccessFile.java index a2f40bce76..f5a3602bf1 100644 --- a/classpath/java/io/RandomAccessFile.java +++ b/classpath/java/io/RandomAccessFile.java @@ -56,6 +56,8 @@ public class RandomAccessFile { throw new ArrayIndexOutOfBoundsException(); copy(peer, position, buffer, offset, length); + + position += length; } private static native void copy(long peer, long position, byte[] buffer, From 90dc5524635ba0775d5f43229cea90202924c2f2 Mon Sep 17 00:00:00 2001 From: Joshua Warner Date: Thu, 3 Nov 2011 12:30:51 -0600 Subject: [PATCH 25/50] implement Process.destroy --- classpath/java-lang.cpp | 10 ++++++++++ classpath/java/lang/Runtime.java | 4 +++- test/Processes.java | 31 +++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 test/Processes.java diff --git a/classpath/java-lang.cpp b/classpath/java-lang.cpp index 4f301572e9..0c8cead9c3 100644 --- a/classpath/java-lang.cpp +++ b/classpath/java-lang.cpp @@ -279,6 +279,11 @@ Java_java_lang_Runtime_waitFor(JNIEnv* e, jclass, jlong pid, jlong tid) return exitCode; } +extern "C" JNIEXPORT void JNICALL +Java_java_lang_Runtime_kill(JNIEnv*, jclass, jlong pid) { + TerminateProcess(reinterpret_cast(pid), 1); +} + Locale getLocale() { const char* lang = ""; const char* reg = ""; @@ -466,6 +471,11 @@ Java_java_lang_Runtime_waitFor(JNIEnv*, jclass, jlong pid, jlong) return exitCode; } +extern "C" JNIEXPORT void JNICALL +Java_java_lang_Runtime_kill(JNIEnv*, jclass, jlong pid) { + kill((pid_t)pid, SIGTERM); +} + Locale getLocale() { Locale fallback; diff --git a/classpath/java/lang/Runtime.java b/classpath/java/lang/Runtime.java index a24f850bf3..bf80507deb 100644 --- a/classpath/java/lang/Runtime.java +++ b/classpath/java/lang/Runtime.java @@ -122,6 +122,8 @@ public class Runtime { private static native void load(String name, boolean mapName); + private static native void kill(long pid); + public native void gc(); public native void exit(int code); @@ -147,7 +149,7 @@ public class Runtime { } public void destroy() { - throw new RuntimeException("not implemented"); + kill(pid); } public InputStream getInputStream() { diff --git a/test/Processes.java b/test/Processes.java new file mode 100644 index 0000000000..372513f0c1 --- /dev/null +++ b/test/Processes.java @@ -0,0 +1,31 @@ +import java.io.IOException; + +public class Processes { + public static void main(String[] args) { + long start = System.currentTimeMillis(); + try { + final Process p = Runtime.getRuntime().exec("sleep 10"); + new Thread() { + public void run() { + try { + Thread.sleep(100); + } catch(InterruptedException e) { + // ignore + } + p.destroy(); + } + }.start(); + try { + p.waitFor(); + } catch(InterruptedException e) { + // ignore + } + long stop = System.currentTimeMillis(); + if(stop - start > 5000) { + throw new RuntimeException("test failed; we didn't kill the process..."); + } + } catch(IOException e) { + throw new RuntimeException(e); + } + } +} From 216b9a05eaf41d1d97d66655e1dbfe0dc0c3755e Mon Sep 17 00:00:00 2001 From: JET Date: Thu, 3 Nov 2011 13:39:24 -0600 Subject: [PATCH 26/50] RandomAccessFile now correctly reports lengths for growing files. --- classpath/java/io/RandomAccessFile.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/classpath/java/io/RandomAccessFile.java b/classpath/java/io/RandomAccessFile.java index f5a3602bf1..2a1080bb4f 100644 --- a/classpath/java/io/RandomAccessFile.java +++ b/classpath/java/io/RandomAccessFile.java @@ -12,25 +12,25 @@ package java.io; public class RandomAccessFile { private long peer; - private long length; + private File file; private long position = 0; - + public RandomAccessFile(String name, String mode) throws FileNotFoundException { if (! mode.equals("r")) throw new IllegalArgumentException(); + file = new File(name); long[] result = new long[2]; open(name, result); peer = result[0]; - length = result[1]; } private static native void open(String name, long[] result) throws FileNotFoundException; public long length() throws IOException { - return length; + return file.length(); } public long getFilePointer() throws IOException { @@ -38,7 +38,7 @@ public class RandomAccessFile { } public void seek(long position) throws IOException { - if (position < 0 || position > length) throw new IOException(); + if (position < 0 || position > length()) throw new IOException(); this.position = position; } @@ -50,7 +50,7 @@ public class RandomAccessFile { if (length == 0) return; - if (position + length > this.length) throw new EOFException(); + if (position + length > this.length()) throw new EOFException(); if (offset < 0 || offset + length > buffer.length) throw new ArrayIndexOutOfBoundsException(); From 2b2a2e94466da48f216216b9783d25eb907ed756 Mon Sep 17 00:00:00 2001 From: JET Date: Thu, 3 Nov 2011 19:30:44 -0600 Subject: [PATCH 27/50] Completion of previous fix. --- classpath/java/io/RandomAccessFile.java | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/classpath/java/io/RandomAccessFile.java b/classpath/java/io/RandomAccessFile.java index 2a1080bb4f..cc3c41e757 100644 --- a/classpath/java/io/RandomAccessFile.java +++ b/classpath/java/io/RandomAccessFile.java @@ -14,23 +14,36 @@ public class RandomAccessFile { private long peer; private File file; private long position = 0; + private long length; public RandomAccessFile(String name, String mode) throws FileNotFoundException { if (! mode.equals("r")) throw new IllegalArgumentException(); file = new File(name); + open(); + } + private void open() throws FileNotFoundException { long[] result = new long[2]; - open(name, result); + open(file.getPath(), result); peer = result[0]; + length = result[1]; } private static native void open(String name, long[] result) throws FileNotFoundException; + private void refresh() throws IOException { + if (file.length() != length) { + close(); + open(); + } + } + public long length() throws IOException { - return file.length(); + refresh(); + return length; } public long getFilePointer() throws IOException { @@ -50,7 +63,9 @@ public class RandomAccessFile { if (length == 0) return; - if (position + length > this.length()) throw new EOFException(); + if (position + length > this.length) { + if (position + length > length()) throw new EOFException(); + } if (offset < 0 || offset + length > buffer.length) throw new ArrayIndexOutOfBoundsException(); From 4026ad208cf226da285b35b8f7060aa26f8ae4fa Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Fri, 4 Nov 2011 08:25:26 -0600 Subject: [PATCH 28/50] update makefile to point to iPhoneOS5.0.sdk --- makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefile b/makefile index 1e7946dc24..af8e057347 100755 --- a/makefile +++ b/makefile @@ -319,7 +319,7 @@ ifeq ($(platform),darwin) converter-cflags += -DOPPOSITE_ENDIAN endif flags = -arch armv6 -isysroot \ - /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/ + /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk/ openjdk-extra-cflags += $(flags) cflags += $(flags) asmflags += $(flags) From 21cc4c6a87c2200140734bae148db7ea4fedce91 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Fri, 4 Nov 2011 08:27:11 -0600 Subject: [PATCH 29/50] implement AbstractList.indexOf --- classpath/java/util/AbstractList.java | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/classpath/java/util/AbstractList.java b/classpath/java/util/AbstractList.java index e1d4f3c307..fb9ccdccfd 100644 --- a/classpath/java/util/AbstractList.java +++ b/classpath/java/util/AbstractList.java @@ -1,4 +1,4 @@ -/* Copyright (c) 2009, Avian Contributors +/* Copyright (c) 2009-2011, Avian Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided @@ -22,4 +22,20 @@ public abstract class AbstractList extends AbstractCollection public ListIterator listIterator() { return new Collections.ArrayListIterator(this); } + + public int indexOf(Object o) { + int i = 0; + for (T v: this) { + if (o == null) { + if (v == null) { + return i; + } + } else if (o.equals(v)) { + return i; + } + + ++ i; + } + return -1; + } } From bcb62af2a1cd7016c06d3e66aba694dcc1fb3eda Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Fri, 4 Nov 2011 09:37:24 -0600 Subject: [PATCH 30/50] fix java-lang.cpp iOS build --- classpath/java-lang.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/classpath/java-lang.cpp b/classpath/java-lang.cpp index c5cd630fbf..bb087560fe 100644 --- a/classpath/java-lang.cpp +++ b/classpath/java-lang.cpp @@ -51,7 +51,9 @@ # endif # include "unistd.h" # include "limits.h" +# include "signal.h" # include "sys/time.h" +# include "sys/types.h" # include "sys/sysctl.h" # include "sys/utsname.h" # include "sys/wait.h" From a2c6cc8882a96cbd0c123f3699119fec0e05f095 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Mon, 7 Nov 2011 15:52:42 -0700 Subject: [PATCH 31/50] implement ConcurrentLinkedQueue.clear --- classpath/java/util/concurrent/ConcurrentLinkedQueue.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/classpath/java/util/concurrent/ConcurrentLinkedQueue.java b/classpath/java/util/concurrent/ConcurrentLinkedQueue.java index aa920311ca..251adadbcc 100644 --- a/classpath/java/util/concurrent/ConcurrentLinkedQueue.java +++ b/classpath/java/util/concurrent/ConcurrentLinkedQueue.java @@ -25,6 +25,11 @@ public class ConcurrentLinkedQueue { private volatile Node head = new Node(null, null); private volatile Node tail = head; + public void clear() { + // todo: can we safely make this O(1)? + while (poll() != null) { } + } + public boolean add(T value) { Node n = new Node(value, null); while (true) { From 6e86ac39db90a11a1d2c628514b2912dfc491503 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Mon, 7 Nov 2011 17:14:41 -0700 Subject: [PATCH 32/50] fix native call marshalling on Apple/ARM When the fourth argument is a 64-bit value on the Apple ARM ABI, it is passed half by register and half on the stack, unlike on Linux where it is passed entirely on the stack. The logic to handle this in arm.h was flawed, and this commit fixes it. --- src/arm.h | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/arm.h b/src/arm.h index a5285f86c3..42fc44e8bf 100644 --- a/src/arm.h +++ b/src/arm.h @@ -146,18 +146,24 @@ dynamicCall(void* function, uintptr_t* arguments, uint8_t* argumentTypes, switch (argumentTypes[ati]) { case DOUBLE_TYPE: case INT64_TYPE: { - if (gprIndex + Alignment <= GprCount) { // pass argument on registers - if (gprIndex % Alignment) { // 8-byte alignment - memset(gprTable + gprIndex, 0, 4); // probably not necessary, but for good luck - ++gprIndex; + if (gprIndex + Alignment <= GprCount) { // pass argument in register(s) + if (Alignment == 1 + and BytesPerWord < 8 + and gprIndex + Alignment == GprCount) + { + gprTable[gprIndex++] = arguments[ai]; + stack[stackIndex++] = arguments[ai + 1]; + } else { + if (gprIndex % Alignment) { + ++gprIndex; + } + + memcpy(gprTable + gprIndex, arguments + ai, 8); + gprIndex += 8 / BytesPerWord; } - - memcpy(gprTable + gprIndex, arguments + ai, 8); - gprIndex += 8 / BytesPerWord; } else { // pass argument on stack gprIndex = GprCount; - if (stackIndex % Alignment) { // 8-byte alignment - memset(stack + stackIndex, 0, 4); // probably not necessary, but for good luck + if (stackIndex % Alignment) { ++stackIndex; } From f1b0d995a84f87de29ae8f84223802cc8aec2098 Mon Sep 17 00:00:00 2001 From: Ben Limmer Date: Wed, 9 Nov 2011 13:18:04 -0700 Subject: [PATCH 33/50] Fixed bug where calling Process.destroy() on a PID that no longer exist kills all processes in Pgroup. --- classpath/java/lang/Runtime.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/classpath/java/lang/Runtime.java b/classpath/java/lang/Runtime.java index bf80507deb..88c8ba71e3 100644 --- a/classpath/java/lang/Runtime.java +++ b/classpath/java/lang/Runtime.java @@ -149,7 +149,9 @@ public class Runtime { } public void destroy() { - kill(pid); + if (pid != 0) { + kill(pid); + } } public InputStream getInputStream() { From 89cdd9d0d275554ea0369824e11186a3ec1bf887 Mon Sep 17 00:00:00 2001 From: Ben Limmer Date: Wed, 9 Nov 2011 13:18:04 -0700 Subject: [PATCH 34/50] Fixed bug where calling Process.destroy() on a PID that no longer exist kills all processes in Pgroup. --- classpath/java/lang/Runtime.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/classpath/java/lang/Runtime.java b/classpath/java/lang/Runtime.java index bf80507deb..88c8ba71e3 100644 --- a/classpath/java/lang/Runtime.java +++ b/classpath/java/lang/Runtime.java @@ -149,7 +149,9 @@ public class Runtime { } public void destroy() { - kill(pid); + if (pid != 0) { + kill(pid); + } } public InputStream getInputStream() { From 4d0b127989049f6f3d717c0fa3b7f86f20a2a2a5 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Thu, 10 Nov 2011 13:10:53 -0700 Subject: [PATCH 35/50] support multiple sequential VM instances with bootimage build Until now, the bootimage build hasn't supported using the Java invocation API to create a VM, destroy it, and create another in the same process. Ideally, we would be able to create multiple VMs simultaneously without any interference between them. In fact, Avian is designed to support this for the most part, but there are a few places we use global, mutable state which prevent this from working. Most notably, the bootimage is modified in-place at runtime, so the best we can do without extensive changes is to clean up the bootimage when the VM is destroyed so it's ready for later instances. Hence this commit. Ultimately, we can move towards a fully reentrant VM by making the bootimage immutable, but this will require some care to avoid performance regressions. Another challenge is our Posix signal handlers, which currently rely on a global handle to the VM, since you can't, to my knowledge, pass a context pointer when registering a signal handler. Thread local variables won't necessarily help, since a thread might attatch to more than one VM at a time. --- src/compile.cpp | 83 +++++++++++++++++++++++++++++++++++++++++++---- src/heap.cpp | 15 ++++++--- src/interpret.cpp | 2 +- src/jnienv.cpp | 2 ++ src/machine.cpp | 42 +++++++++++++++--------- src/machine.h | 24 +++++++++++--- 6 files changed, 137 insertions(+), 31 deletions(-) diff --git a/src/compile.cpp b/src/compile.cpp index 4d7d7cecdd..25fa2c5ab5 100644 --- a/src/compile.cpp +++ b/src/compile.cpp @@ -2011,7 +2011,7 @@ findExceptionHandler(Thread* t, object method, void* ip) if (key >= start and key < end) { object catchType = arrayBody(t, table, i + 1); - if (catchType == 0 or instanceOf(t, catchType, t->exception)) { + if (exceptionMatch(t, catchType, t->exception)) { return compiled + intArrayBody(t, index, (i * 3) + 2); } } @@ -8249,7 +8249,7 @@ class SignalHandler: public System::SignalHandler { t->exception = vm::root(t, root); } - //printTrace(t, t->exception); + // printTrace(t, t->exception); object continuation; findUnwindTarget(t, ip, frame, stack, &continuation); @@ -9237,6 +9237,52 @@ fixupHeap(MyThread* t UNUSED, uintptr_t* map, unsigned size, uintptr_t* heap) } } +void +resetClassRuntimeState(Thread* t, object c, uintptr_t* heap, unsigned heapSize) +{ + classRuntimeDataIndex(t, c) = 0; + + if (classArrayElementSize(t, c) == 0) { + object staticTable = classStaticTable(t, c); + if (staticTable) { + for (unsigned i = 0; i < singletonCount(t, staticTable); ++i) { + if (singletonIsObject(t, staticTable, i) + and (reinterpret_cast + (singletonObject(t, staticTable, i)) < heap or + reinterpret_cast + (singletonObject(t, staticTable, i)) > heap + heapSize)) + { + singletonObject(t, staticTable, i) = 0; + } + } + } + } + + if (classMethodTable(t, c)) { + for (unsigned i = 0; i < arrayLength(t, classMethodTable(t, c)); ++i) { + object m = arrayBody(t, classMethodTable(t, c), i); + + methodNativeID(t, m) = 0; + methodRuntimeDataIndex(t, m) = 0; + + if (methodVmFlags(t, m) & ClassInitFlag) { + classVmFlags(t, c) |= NeedInitFlag; + classVmFlags(t, c) &= ~InitErrorFlag; + } + } + } + + t->m->processor->initVtable(t, c); +} + +void +resetRuntimeState(Thread* t, object map, uintptr_t* heap, unsigned heapSize) +{ + for (HashMapIterator it(t, map); it.hasMore();) { + resetClassRuntimeState(t, tripleSecond(t, it.next()), heap, heapSize); + } +} + void fixupMethods(Thread* t, object map, BootImage* image UNUSED, uint8_t* code) { @@ -9339,7 +9385,11 @@ boot(MyThread* t, BootImage* image, uint8_t* code) // fprintf(stderr, "code from %p to %p\n", // code, code + image->codeSize); - fixupHeap(t, heapMap, heapMapSizeInWords, heap); + static bool fixed = false; + + if (not fixed) { + fixupHeap(t, heapMap, heapMapSizeInWords, heap); + } t->m->heap->setImmortalHeap(heap, image->heapSize / BytesPerWord); @@ -9384,11 +9434,30 @@ boot(MyThread* t, BootImage* image, uint8_t* code) findThunks(t, image, code); - fixupVirtualThunks(t, code); + if (fixed) { + resetRuntimeState + (t, classLoaderMap(t, root(t, Machine::BootLoader)), heap, + image->heapSize); - fixupMethods - (t, classLoaderMap(t, root(t, Machine::BootLoader)), image, code); - fixupMethods(t, classLoaderMap(t, root(t, Machine::AppLoader)), image, code); + resetRuntimeState + (t, classLoaderMap(t, root(t, Machine::AppLoader)), heap, + image->heapSize); + + for (unsigned i = 0; i < arrayLength(t, t->m->types); ++i) { + resetClassRuntimeState + (t, type(t, static_cast(i)), heap, image->heapSize); + } + } else { + fixupVirtualThunks(t, code); + + fixupMethods + (t, classLoaderMap(t, root(t, Machine::BootLoader)), image, code); + + fixupMethods + (t, classLoaderMap(t, root(t, Machine::AppLoader)), image, code); + } + + fixed = true; setRoot(t, Machine::BootstrapClassMap, makeHashMap(t, 0, 0)); } diff --git a/src/heap.cpp b/src/heap.cpp index e441640364..b9f20f82d8 100644 --- a/src/heap.cpp +++ b/src/heap.cpp @@ -848,13 +848,20 @@ bitset(Context* c UNUSED, void* o) void free(Context* c, Fixie** fixies) { - for (Fixie** p = fixies; *p;) { - Fixie* f = *p; + for (Fixie* p = *fixies; p;) { + Fixie* f = p; + p = f->next; if (f->immortal()) { - p = &(f->next); + if (DebugFixies) { + fprintf(stderr, "reset immortal fixie %p\n", f); + } + memset(f->mask(), 0, Fixie::maskSize(f->size, f->hasMask)); + f->next = 0; + f->handle = 0; + f->marked = false; + f->dirty = false; } else { - *p = f->next; if (DebugFixies) { fprintf(stderr, "free fixie %p\n", f); } diff --git a/src/interpret.cpp b/src/interpret.cpp index 65293463f0..6e04cd4942 100644 --- a/src/interpret.cpp +++ b/src/interpret.cpp @@ -698,7 +698,7 @@ findExceptionHandler(Thread* t, object method, unsigned ip) } } - if (catchType == 0 or instanceOf(t, catchType, t->exception)) { + if (exceptionMatch(t, catchType, t->exception)) { return eh; } } diff --git a/src/jnienv.cpp b/src/jnienv.cpp index 4d4518febd..34f82efc65 100644 --- a/src/jnienv.cpp +++ b/src/jnienv.cpp @@ -3024,6 +3024,8 @@ boot(Thread* t, uintptr_t*) setRoot(t, Machine::OutOfMemoryError, makeThrowable(t, Machine::OutOfMemoryErrorType)); + setRoot(t, Machine::Shutdown, makeThrowable(t, Machine::ThrowableType)); + setRoot(t, Machine::FinalizerThread, t->m->classpath->makeThread(t, t)); threadDaemon(t, root(t, Machine::FinalizerThread)) = true; diff --git a/src/machine.cpp b/src/machine.cpp index bdfdef42a3..6f918e7ca6 100644 --- a/src/machine.cpp +++ b/src/machine.cpp @@ -138,35 +138,37 @@ dispose(Thread* t, Thread* o, bool remove) } void -joinAll(Thread* m, Thread* o) +visitAll(Thread* m, Thread* o, void (*visit)(Thread*, Thread*)) { for (Thread* p = o->child; p;) { Thread* child = p; p = p->peer; - joinAll(m, child); + visitAll(m, child, visit); } - join(m, o); + visit(m, o); } void -disposeAll(Thread* m, Thread* o) +disposeNoRemove(Thread* m, Thread* o) { - for (Thread* p = o->child; p;) { - Thread* child = p; - p = p->peer; - disposeAll(m, child); - } - dispose(m, o, false); } +void +interruptDaemon(Thread* m, Thread* o) +{ + if (o->flags & Thread::DaemonFlag) { + interrupt(m, o); + } +} + void turnOffTheLights(Thread* t) { expect(t, t->m->liveCount == 1); - joinAll(t, t->m->rootThread); + visitAll(t, t->m->rootThread, join); enter(t, Thread::ExitState); @@ -215,7 +217,7 @@ turnOffTheLights(Thread* t) Machine* m = t->m; - disposeAll(t, t->m->rootThread); + visitAll(t, t->m->rootThread, disposeNoRemove); System* s = m->system; Heap* h = m->heap; @@ -2443,6 +2445,7 @@ Machine::Machine(System* system, Heap* heap, Finder* bootFinder, collecting(false), triedBuiltinOnLoad(false), dumpedHeapOnOOM(false), + alive(true), heapPoolIndex(0) { heap->setClient(heapClient); @@ -2694,6 +2697,17 @@ shutDown(Thread* t) } } } + + // interrupt daemon threads and tell them to die + + // todo: be more aggressive about killing daemon threads, e.g. at + // any GC point, not just at waits/sleeps + { ACQUIRE(t, t->m->stateLock); + + t->m->alive = false; + + visitAll(t, t->m->rootThread, interruptDaemon); + } } void @@ -3247,9 +3261,7 @@ classInitializer(Thread* t, object class_) { object o = arrayBody(t, classMethodTable(t, class_), i); - if (vm::strcmp(reinterpret_cast(""), - &byteArrayBody(t, methodName(t, o), 0)) == 0) - { + if (methodVmFlags(t, o) & ClassInitFlag) { return o; } } diff --git a/src/machine.h b/src/machine.h index 70f3d7295e..b11ca98667 100644 --- a/src/machine.h +++ b/src/machine.h @@ -157,8 +157,7 @@ const unsigned ContinuationFlag = 1 << 11; // method vmFlags: const unsigned ClassInitFlag = 1 << 0; -const unsigned CompiledFlag = 1 << 1; -const unsigned ConstructorFlag = 1 << 2; +const unsigned ConstructorFlag = 1 << 1; #ifndef JNI_VERSION_1_6 #define JNI_VERSION_1_6 0x00010006 @@ -1266,6 +1265,7 @@ class Machine { ArithmeticException, ArrayIndexOutOfBoundsException, OutOfMemoryError, + Shutdown, VirtualFileFinders, VirtualFiles }; @@ -1322,6 +1322,7 @@ class Machine { bool collecting; bool triedBuiltinOnLoad; bool dumpedHeapOnOOM; + bool alive; JavaVMVTable javaVMVTable; JNIEnvVTable jniEnvVTable; uintptr_t* heapPool[ThreadHeapPoolSize]; @@ -1357,6 +1358,9 @@ run(Thread* t, uint64_t (*function)(Thread*, uintptr_t*), void checkDaemon(Thread* t); +object& +root(Thread* t, Machine::Root root); + extern "C" uint64_t vmRun(uint64_t (*function)(Thread*, uintptr_t*), uintptr_t* arguments, void* checkpoint); @@ -1507,7 +1511,7 @@ class Thread { vm::run(t, runThread, 0); - if (t->exception) { + if (t->exception and t->exception != root(t, Machine::Shutdown)) { printTrace(t, t->exception); } @@ -3182,7 +3186,11 @@ wait(Thread* t, object o, int64_t milliseconds) bool interrupted = monitorWait(t, m, milliseconds); if (interrupted) { - throwNew(t, Machine::InterruptedExceptionType); + if (t->m->alive or (t->flags & Thread::DaemonFlag) == 0) { + throwNew(t, Machine::InterruptedExceptionType); + } else { + throw_(t, root(t, Machine::Shutdown)); + } } } else { throwNew(t, Machine::IllegalMonitorStateExceptionType); @@ -3256,6 +3264,14 @@ getAndClearInterrupted(Thread* t, Thread* target) } } +inline bool +exceptionMatch(Thread* t, object type, object exception) +{ + return type == 0 + or (exception != root(t, Machine::Shutdown) + and instanceOf(t, type, t->exception)); +} + object intern(Thread* t, object s); From 7d185dd27d92bc1bddcd2450d5e8cad65de14856 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Thu, 10 Nov 2011 15:27:34 -0700 Subject: [PATCH 36/50] revert heap.cpp part of last commit That change seems to be causing crashes under certain circumstances, so I'm reverting it for now while I debug. --- src/heap.cpp | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/heap.cpp b/src/heap.cpp index b9f20f82d8..e441640364 100644 --- a/src/heap.cpp +++ b/src/heap.cpp @@ -848,20 +848,13 @@ bitset(Context* c UNUSED, void* o) void free(Context* c, Fixie** fixies) { - for (Fixie* p = *fixies; p;) { - Fixie* f = p; - p = f->next; + for (Fixie** p = fixies; *p;) { + Fixie* f = *p; if (f->immortal()) { - if (DebugFixies) { - fprintf(stderr, "reset immortal fixie %p\n", f); - } - memset(f->mask(), 0, Fixie::maskSize(f->size, f->hasMask)); - f->next = 0; - f->handle = 0; - f->marked = false; - f->dirty = false; + p = &(f->next); } else { + *p = f->next; if (DebugFixies) { fprintf(stderr, "free fixie %p\n", f); } From 3e5a7d9a418cdb2184fb96d272630eb645576a74 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Fri, 11 Nov 2011 17:37:40 -0700 Subject: [PATCH 37/50] reset immortal fixed objects when heap is disposed This should ensure that the bootimage is ready to reuse by another instance of the VM later on. --- src/heap.cpp | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/heap.cpp b/src/heap.cpp index e441640364..b99de70de7 100644 --- a/src/heap.cpp +++ b/src/heap.cpp @@ -560,7 +560,7 @@ fixie(void* body) } void -free(Context* c, Fixie** fixies); +free(Context* c, Fixie** fixies, bool resetImmortal = false); class Context { public: @@ -630,9 +630,9 @@ class Context { } void disposeFixies() { - free(this, &tenuredFixies); - free(this, &dirtyTenuredFixies); - free(this, &fixies); + free(this, &tenuredFixies, true); + free(this, &dirtyTenuredFixies, true); + free(this, &fixies, true); } System* system; @@ -846,13 +846,25 @@ bitset(Context* c UNUSED, void* o) } void -free(Context* c, Fixie** fixies) +free(Context* c, Fixie** fixies, bool resetImmortal) { for (Fixie** p = fixies; *p;) { Fixie* f = *p; if (f->immortal()) { - p = &(f->next); + if (resetImmortal) { + if (DebugFixies) { + fprintf(stderr, "reset immortal fixie %p\n", f); + } + *p = f->next; + memset(f->mask(), 0, Fixie::maskSize(f->size, f->hasMask)); + f->next = 0; + f->handle = 0; + f->marked = false; + f->dirty = false; + } else { + p = &(f->next); + } } else { *p = f->next; if (DebugFixies) { From e4c1f923b5839888755c242d55a2ab012b710302 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Fri, 18 Nov 2011 08:38:19 -0700 Subject: [PATCH 38/50] fix GC safety bug in resolveObjectArrayClass The call to getClassRuntimeData may trigger a GC, so we must mark the local variables to be visited. --- src/machine.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/machine.cpp b/src/machine.cpp index 6f918e7ca6..5084bbcf53 100644 --- a/src/machine.cpp +++ b/src/machine.cpp @@ -3810,6 +3810,9 @@ initClass(Thread* t, object c) object resolveObjectArrayClass(Thread* t, object loader, object elementClass) { + PROTECT(t, loader); + PROTECT(t, elementClass); + { object arrayClass = classRuntimeDataArrayClass (t, getClassRuntimeData(t, elementClass)); if (arrayClass) { @@ -3817,9 +3820,6 @@ resolveObjectArrayClass(Thread* t, object loader, object elementClass) } } - PROTECT(t, loader); - PROTECT(t, elementClass); - object elementSpec = className(t, elementClass); PROTECT(t, elementSpec); From b29db7fece44f48a9bce6d4fd6fa642d313e1ad9 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Sat, 19 Nov 2011 19:22:34 -0700 Subject: [PATCH 39/50] fix OpenJDK 7 build error OpenJDK 7's javac doesn't seem to like that we're accessing Enum.ordinal in compareTo when it's private. --- classpath/java/lang/Enum.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classpath/java/lang/Enum.java b/classpath/java/lang/Enum.java index e09bd73c2c..cf1d033cc3 100644 --- a/classpath/java/lang/Enum.java +++ b/classpath/java/lang/Enum.java @@ -14,7 +14,7 @@ import java.lang.reflect.Method; public abstract class Enum> implements Comparable { private final String name; - private final int ordinal; + protected final int ordinal; public Enum(String name, int ordinal) { this.name = name; From 1c85ea8a6ed736eac14144d92f570e689f7ac672 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Sat, 19 Nov 2011 19:26:25 -0700 Subject: [PATCH 40/50] fix GCC 4.6 LTO build On Ubuntu 11.10, the optimized build was breaking, apparently because it was eliminating most of the symbols defined in assembly code (e.g. vmJump) as unreachable when linking libjvm.so, which left avian-dynamic unlinkable due to an unresolved symbol. The solution in this commit is to export makeSystem and makeFinder from libjvm.so rather than build redundant versions of finder.cpp and posix.cpp/windows.cpp into avian-dynamic like we've been doing. This avoids the whole problem of vmJump reachability and reduces the size of avian-dynamic at the same time. This commit also turns off LTO for the avian-dynamic link since we get odd undefined symbol errors about libc-defined symbols otherwise. This may merit future investigation, but avian-dynamic is so small and simple that there's no need to optimize it anyway. --- makefile | 9 +++++---- src/finder.cpp | 2 +- src/posix.cpp | 2 +- src/windows.cpp | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/makefile b/makefile index af8e057347..89990514f0 100755 --- a/makefile +++ b/makefile @@ -447,6 +447,7 @@ ifeq ($(use-lto),true) ifeq ($(shell expr 4 \< $(gcc-major) \ \| \( 4 \<= $(gcc-major) \& 6 \<= $(gcc-minor) \)),1) optimization-cflags += -flto + no-lto = -fno-lto lflags += $(optimization-cflags) endif endif @@ -588,9 +589,7 @@ lflags += $(extra-lflags) driver-source = $(src)/main.cpp driver-object = $(build)/main.o driver-dynamic-objects = \ - $(build)/main-dynamic.o \ - $(build)/$(system).o \ - $(build)/finder.o + $(build)/main-dynamic.o boot-source = $(src)/boot.cpp boot-object = $(build)/boot.o @@ -958,6 +957,8 @@ else endif $(strip) $(strip-all) $(@) +# todo: the $(no-lto) flag below is due to odd undefined reference errors on +# Ubuntu 11.10 which may be fixable without disabling LTO. $(executable-dynamic): $(driver-dynamic-objects) $(dynamic-library) @echo "linking $(@)" ifdef msvc @@ -966,7 +967,7 @@ ifdef msvc -MANIFESTFILE:$(@).manifest $(mt) -manifest $(@).manifest -outputresource:"$(@);1" else - $(ld) $(driver-dynamic-objects) -L$(build) -ljvm $(lflags) -o $(@) + $(ld) $(driver-dynamic-objects) -L$(build) -ljvm $(lflags) $(no-lto) -o $(@) endif $(strip) $(strip-all) $(@) diff --git a/src/finder.cpp b/src/finder.cpp index 7257fa2603..f53738e9f4 100644 --- a/src/finder.cpp +++ b/src/finder.cpp @@ -920,7 +920,7 @@ class MyFinder: public Finder { namespace vm { -Finder* +JNIEXPORT Finder* makeFinder(System* s, Allocator* a, const char* path, const char* bootLibrary) { return new (a->allocate(sizeof(MyFinder))) MyFinder(s, a, path, bootLibrary); diff --git a/src/posix.cpp b/src/posix.cpp index 513d8c8fb7..f695b0b21d 100644 --- a/src/posix.cpp +++ b/src/posix.cpp @@ -999,7 +999,7 @@ handleSignal(int signal, siginfo_t*, void* context) namespace vm { -System* +JNIEXPORT System* makeSystem(const char*) { return new (malloc(sizeof(MySystem))) MySystem(); diff --git a/src/windows.cpp b/src/windows.cpp index 969dabda3a..8f90f6618b 100644 --- a/src/windows.cpp +++ b/src/windows.cpp @@ -986,7 +986,7 @@ handleException(LPEXCEPTION_POINTERS e) namespace vm { -System* +JNIEXPORT System* makeSystem(const char* crashDumpDirectory) { return new (malloc(sizeof(MySystem))) MySystem(crashDumpDirectory); From b3850ac76db7897a86e5cbac29e72092c8cc75e5 Mon Sep 17 00:00:00 2001 From: Ben Limmer Date: Wed, 28 Dec 2011 15:52:53 -0700 Subject: [PATCH 41/50] Initial attempt at resolving SWT3.7 missing operatons in Avian. Everything seems to be working except floatToIntBits, hence the test case failing. --- classpath/java/io/File.java | 4 ++++ classpath/java/lang/Float.java | 17 +++++++++++++++-- classpath/java/net/URL.java | 18 ++++++++++++++++++ test/Floats.java | 27 +++++++++++++++++++++++++++ 4 files changed, 64 insertions(+), 2 deletions(-) diff --git a/classpath/java/io/File.java b/classpath/java/io/File.java index 51e006e53f..9342627bd5 100644 --- a/classpath/java/io/File.java +++ b/classpath/java/io/File.java @@ -61,6 +61,10 @@ public class File implements Serializable { return isFile(path); } + public boolean isAbsolute() { + return path.equals(toAbsolutePath(path)); + } + private static native boolean canRead(String path); public boolean canRead() { diff --git a/classpath/java/lang/Float.java b/classpath/java/lang/Float.java index 1e2c62972b..d466cca2a3 100644 --- a/classpath/java/lang/Float.java +++ b/classpath/java/lang/Float.java @@ -12,8 +12,10 @@ package java.lang; public final class Float extends Number { public static final Class TYPE = Class.forCanonicalName("F"); - - private final float value; + private static final int EXP_BIT_MASK = 0x7F800000; + private static final int SIGNIF_BIT_MASK = 0x007FFFFF; + + private final float value; public Float(String value) { this.value = parseFloat(value); @@ -88,6 +90,17 @@ public final class Float extends Number { throw new NumberFormatException(s); } } + + public static int floatToIntBits(float value) { + int result = floatToRawIntBits(value); + + // Check for NaN based on values of bit fields, maximum + // exponent and nonzero significand. + if (((result & EXP_BIT_MASK) == EXP_BIT_MASK) && (result & SIGNIF_BIT_MASK) != 0) { + result = 0x7fc00000; + } + return result; + } public static native int floatToRawIntBits(float value); diff --git a/classpath/java/net/URL.java b/classpath/java/net/URL.java index 874eec964b..eb46c61860 100644 --- a/classpath/java/net/URL.java +++ b/classpath/java/net/URL.java @@ -19,6 +19,8 @@ public final class URL { private String host; private int port; private String file; + private String path; + private String query; private String ref; public URL(String s) throws MalformedURLException { @@ -55,6 +57,14 @@ public final class URL { public String getRef() { return ref; } + + public String getPath() { + return path; + } + + public String getQuery() { + return query; + } public URLConnection openConnection() throws IOException { return handler.openConnection(this); @@ -90,5 +100,13 @@ public final class URL { this.port = port; this.file = file; this.ref = ref; + + int q = file.lastIndexOf('?'); + if (q != -1) { + this.query = file.substring(q + 1); + this.path = file.substring(0, q); + } else { + this.path = file; + } } } diff --git a/test/Floats.java b/test/Floats.java index 38ae3121b1..e27e703d40 100644 --- a/test/Floats.java +++ b/test/Floats.java @@ -211,5 +211,32 @@ public class Floats { double d = (double) z; expect(d == 12345.0); } + + { + int NaN = 0x7F800001; + int result = Float.floatToIntBits(NaN); + int expected = 0x7fc00000; + System.out.println("NaN integer is: " + result + " and we were expecting it to be: " + expected); + expect(result == expected); + } + + { + int number = 0x00800001; + int result = Float.floatToIntBits(number); + System.out.println("Number1 is: " + result); + + expect(result == number); + + + } + + { + int number = 0x80800003; + int result = Float.floatToIntBits(number); + System.out.println("Number2 is: " + result); + expect(result == number); + + + } } } From f5a168f5841db6c0946e52a1a6c955187a3ad2d6 Mon Sep 17 00:00:00 2001 From: Seth Goings Date: Wed, 28 Dec 2011 23:58:33 -0700 Subject: [PATCH 42/50] Fixed tests for floatToIntBits. Probably want to add more comprehensive tests tomorrow (or today... as of 2 minutes) --- test/Floats.java | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/test/Floats.java b/test/Floats.java index e27e703d40..2494088b01 100644 --- a/test/Floats.java +++ b/test/Floats.java @@ -212,31 +212,26 @@ public class Floats { expect(d == 12345.0); } - { - int NaN = 0x7F800001; + { + int orig = 0x7f800001; + float NaN = Float.intBitsToFloat(orig); int result = Float.floatToIntBits(NaN); int expected = 0x7fc00000; - System.out.println("NaN integer is: " + result + " and we were expecting it to be: " + expected); expect(result == expected); } { - int number = 0x00800001; + int orig = 0x00800001; + float number = Float.intBitsToFloat(orig); int result = Float.floatToIntBits(number); - System.out.println("Number1 is: " + result); - - expect(result == number); - - + expect(result == orig); } { - int number = 0x80800003; + int orig = 0x80800003; + float number = Float.intBitsToFloat(orig); int result = Float.floatToIntBits(number); - System.out.println("Number2 is: " + result); - expect(result == number); - - + expect(result == orig); } } } From a0c12ad2595f9b52c3b2b82b81c2e17ed9bd9268 Mon Sep 17 00:00:00 2001 From: Seth Goings Date: Thu, 29 Dec 2011 09:51:44 -0700 Subject: [PATCH 43/50] Added another floatToIntBits test --- test/Floats.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/test/Floats.java b/test/Floats.java index 2494088b01..775b391ffd 100644 --- a/test/Floats.java +++ b/test/Floats.java @@ -211,7 +211,8 @@ public class Floats { double d = (double) z; expect(d == 12345.0); } - + + // Test floatToIntBits { int orig = 0x7f800001; float NaN = Float.intBitsToFloat(orig); @@ -220,6 +221,14 @@ public class Floats { expect(result == expected); } + { + int orig = 0x7f801001; + float NaN = Float.intBitsToFloat(orig); + int result = Float.floatToIntBits(NaN); + int expected = 0x7fc00000; + expect(result == expected); + } + { int orig = 0x00800001; float number = Float.intBitsToFloat(orig); From d76191d2bb9185ce8ed82e19b0caf77875d5e49c Mon Sep 17 00:00:00 2001 From: Ben Limmer Date: Thu, 29 Dec 2011 10:17:01 -0700 Subject: [PATCH 44/50] Adding unit tests for File.isAbsolute() method. --- test/Files.java | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 test/Files.java diff --git a/test/Files.java b/test/Files.java new file mode 100644 index 0000000000..6bf01dadaa --- /dev/null +++ b/test/Files.java @@ -0,0 +1,29 @@ +import java.io.File; + +public class Files { + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + private static void isAbsoluteTest(boolean absolutePath) { + File file = new File("test.txt"); + if (absolutePath) { + file = file.getAbsoluteFile(); + } + + boolean isAbsolute = file.isAbsolute(); + + if (absolutePath) { + expect(isAbsolute); + } else { + expect(!isAbsolute); + } + + } + + public static void main(String[] args) { + isAbsoluteTest(true); + isAbsoluteTest(false); + } + +} From 51ae790e5492b9d18c10a61f0ca4e7eaee8ac3a1 Mon Sep 17 00:00:00 2001 From: Seth Goings Date: Thu, 29 Dec 2011 10:36:33 -0700 Subject: [PATCH 45/50] Added UrlTest.java to tests dir --- test/UrlTest.java | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 test/UrlTest.java diff --git a/test/UrlTest.java b/test/UrlTest.java new file mode 100644 index 0000000000..35281b367a --- /dev/null +++ b/test/UrlTest.java @@ -0,0 +1,42 @@ +import java.net.MalformedURLException; +import java.net.URL; + +public class UrlTest { + private static String query="var1=val1&var2=val2"; + private static String path="testpath"; + private static String domain="file://www.readytalk.com"; + private static String file=path + "?" + query; + private static URL url; + + private static void expect(boolean v) { + if (! v) throw new RuntimeException(); + } + + private static void setupURL() throws MalformedURLException { + StringBuilder builder = new StringBuilder(); + builder.append(domain); + builder.append("/"); + builder.append(file); + url = new URL(builder.toString()); + } + + private static void testGetPath() { + expect(url.getPath().equals(path)); + } + + private static void testGetFile() { + expect(url.getFile().equals(file)); + } + + private static void testGetQuery() { + expect(url.getQuery().equals(query)); + } + + public static void main(String[] args) throws MalformedURLException { + setupURL(); + testGetPath(); + testGetFile(); + testGetQuery(); + } + +} From 7022b23b26455e86c746ec0aac29a93e58beeef6 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Thu, 12 Jan 2012 10:45:05 -0700 Subject: [PATCH 46/50] add AbstractCollection.toString implementation --- classpath/java/util/AbstractCollection.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/classpath/java/util/AbstractCollection.java b/classpath/java/util/AbstractCollection.java index 58a034765f..54cd7288df 100644 --- a/classpath/java/util/AbstractCollection.java +++ b/classpath/java/util/AbstractCollection.java @@ -72,4 +72,7 @@ public abstract class AbstractCollection implements Collection { public abstract Iterator iterator(); + public String toString() { + return Collections.toString(this); + } } From 0aa5755187f294b55c81965289f39128acc8d5b5 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Thu, 12 Jan 2012 11:00:58 -0700 Subject: [PATCH 47/50] call C library free directly instead of System::free where possible There was a subtle race condition in the VM shutdown process such that a System::Thread would be disposed after the System instance it was created under has been disposed, in which case doing a virtual call to System::free with that instance would potentially cause a crash. The solution is to just call the C library version of free directly, since that's all System::free does. --- src/machine.cpp | 9 +++++++++ src/posix.cpp | 16 ++++++++-------- src/windows.cpp | 14 +++++++------- 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/machine.cpp b/src/machine.cpp index 5084bbcf53..b4db6ac1fa 100644 --- a/src/machine.cpp +++ b/src/machine.cpp @@ -45,6 +45,15 @@ void join(Thread* t, Thread* o) { if (t != o) { + // todo: There's potentially a leak here on systems where we must + // call join on a thread in order to clean up all resources + // associated with it. If a thread has already been zombified by + // the time we get here, acquireSystem will return false, which + // means we can't safely join it because the System::Thread may + // already have been disposed. In that case, the thread has + // already exited (or will soon), but the OS will never free all + // its resources because it doesn't know we're completely done + // with it. if (acquireSystem(t, o)) { o->systemThread->join(); releaseSystem(t, o); diff --git a/src/posix.cpp b/src/posix.cpp index f695b0b21d..074c6b379c 100644 --- a/src/posix.cpp +++ b/src/posix.cpp @@ -169,7 +169,7 @@ class MySystem: public System { } virtual void dispose() { - s->free(this); + ::free(this); } pthread_t thread; @@ -197,7 +197,7 @@ class MySystem: public System { virtual void dispose() { pthread_mutex_destroy(&mutex); - s->free(this); + ::free(this); } System* s; @@ -410,7 +410,7 @@ class MySystem: public System { virtual void dispose() { expect(s, owner_ == 0); pthread_mutex_destroy(&mutex); - s->free(this); + ::free(this); } System* s; @@ -441,7 +441,7 @@ class MySystem: public System { int r UNUSED = pthread_key_delete(key); expect(s, r == 0); - s->free(this); + ::free(this); } System* s; @@ -468,7 +468,7 @@ class MySystem: public System { if (start_) { munmap(start_, length_); } - s->free(this); + ::free(this); } System* s; @@ -494,7 +494,7 @@ class MySystem: public System { if (directory) { closedir(directory); } - s->free(this); + ::free(this); } System* s; @@ -541,10 +541,10 @@ class MySystem: public System { } if (name_) { - s->free(name_); + ::free(const_cast(name_)); } - s->free(this); + ::free(this); } System* s; diff --git a/src/windows.cpp b/src/windows.cpp index 8f90f6618b..42a79d3024 100644 --- a/src/windows.cpp +++ b/src/windows.cpp @@ -119,7 +119,7 @@ class MySystem: public System { CloseHandle(event); CloseHandle(mutex); CloseHandle(thread); - s->free(this); + ::free(this); } HANDLE thread; @@ -150,7 +150,7 @@ class MySystem: public System { virtual void dispose() { CloseHandle(mutex); - s->free(this); + ::free(this); } System* s; @@ -377,7 +377,7 @@ class MySystem: public System { virtual void dispose() { assert(s, owner_ == 0); CloseHandle(mutex); - s->free(this); + ::free(this); } System* s; @@ -408,7 +408,7 @@ class MySystem: public System { bool r UNUSED = TlsFree(key); assert(s, r); - s->free(this); + ::free(this); } System* s; @@ -472,7 +472,7 @@ class MySystem: public System { if (handle and handle != INVALID_HANDLE_VALUE) { FindClose(handle); } - s->free(this); + ::free(this); } System* s; @@ -523,10 +523,10 @@ class MySystem: public System { } if (name_) { - s->free(name_); + ::free(const_cast(name_)); } - s->free(this); + ::free(this); } System* s; From 929315e1f21235502416efd82976f39b952f02a9 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Fri, 13 Jan 2012 16:51:39 -0700 Subject: [PATCH 48/50] avoid crash when parsing certain abstract classes which declare no methods --- src/machine.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/machine.cpp b/src/machine.cpp index b4db6ac1fa..fbf8ca2511 100644 --- a/src/machine.cpp +++ b/src/machine.cpp @@ -1673,13 +1673,17 @@ parseMethodTable(Thread* t, Stream& s, object class_, object pool) set(t, addendum, ClassAddendumMethodTable, classMethodTable(t, class_)); - unsigned oldLength = arrayLength(t, classMethodTable(t, class_)); + unsigned oldLength = classMethodTable(t, class_) ? + arrayLength(t, classMethodTable(t, class_)) : 0; + object newMethodTable = makeArray (t, oldLength + listSize(t, abstractVirtuals)); - memcpy(&arrayBody(t, newMethodTable, 0), - &arrayBody(t, classMethodTable(t, class_), 0), - oldLength * sizeof(object)); + if (oldLength) { + memcpy(&arrayBody(t, newMethodTable, 0), + &arrayBody(t, classMethodTable(t, class_), 0), + oldLength * sizeof(object)); + } mark(t, newMethodTable, ArrayBody, oldLength); From 49d19456d07c1ec547c0a9eda6c08d9c116195f4 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Sat, 14 Jan 2012 20:21:42 -0700 Subject: [PATCH 49/50] fix infinite wait in Unsafe.park There were a couple of problems with the Avian_sun_misc_Unsafe_park implementation in classpath-openjdk.cpp. First, the wait time should be interpreted as milliseconds if absolute, but as nanoseconds otherwise, whereas we were treating it as milliseconds in both cases. Second, there was no mechanism to exit the while loop after the specified time; the only way we could exit was via an unpark or interrupt. --- src/classpath-openjdk.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/classpath-openjdk.cpp b/src/classpath-openjdk.cpp index 8d70b860dd..ecbb86a0a0 100644 --- a/src/classpath-openjdk.cpp +++ b/src/classpath-openjdk.cpp @@ -2747,17 +2747,27 @@ Avian_sun_misc_Unsafe_park bool absolute = arguments[1]; int64_t time; memcpy(&time, arguments + 2, 8); + int64_t then = t->m->system->now(); + if (absolute) { - time -= t->m->system->now(); + time -= then; if (time <= 0) { return; } + } else { + time /= 1000 * 1000; } monitorAcquire(t, local::interruptLock(t, t->javaThread)); - while (not (threadUnparked(t, t->javaThread) - or monitorWait(t, local::interruptLock(t, t->javaThread), time))) - { } + while (time > 0 + and (not (threadUnparked(t, t->javaThread) + or monitorWait + (t, local::interruptLock(t, t->javaThread), time)))) + { + int64_t now = t->m->system->now(); + time -= now - then; + then = now; + } threadUnparked(t, t->javaThread) = false; monitorRelease(t, local::interruptLock(t, t->javaThread)); } From d29513c65325b3af4bbbf6d558884a15195c9264 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Sun, 15 Jan 2012 10:02:36 -0700 Subject: [PATCH 50/50] fix Avian_sun_misc_Unsafe_compareAndSwapLong for platforms without atomicCompareAndSwap64 We never define atomicCompareAndSwap64 for ARM or PowerPC, and apparently only very recent ARM chips support it, so we must fall back to synchronization-based emulation. --- makefile | 5 ++++- src/classpath-openjdk.cpp | 12 +++++++++++- src/x86.h | 2 ++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/makefile b/makefile index 89990514f0..218216f537 100755 --- a/makefile +++ b/makefile @@ -246,7 +246,8 @@ ifeq ($(arch),arm) ifeq ($(build-platform),darwin) ios = true else - cflags += -marm -Wno-psabi + no-psabi = -Wno-psabi + cflags += -marm $(no-psabi) endif ifneq ($(arch),$(build-arch)) @@ -586,6 +587,8 @@ endif cflags += $(extra-cflags) lflags += $(extra-lflags) +openjdk-cflags += $(extra-cflags) + driver-source = $(src)/main.cpp driver-object = $(build)/main.o driver-dynamic-objects = \ diff --git a/src/classpath-openjdk.cpp b/src/classpath-openjdk.cpp index ecbb86a0a0..2b12362a6c 100644 --- a/src/classpath-openjdk.cpp +++ b/src/classpath-openjdk.cpp @@ -2594,15 +2594,25 @@ Avian_sun_misc_Unsafe_compareAndSwapObject extern "C" JNIEXPORT int64_t JNICALL Avian_sun_misc_Unsafe_compareAndSwapLong -(Thread*, object, uintptr_t* arguments) +(Thread* t UNUSED, object, uintptr_t* arguments) { object target = reinterpret_cast(arguments[1]); int64_t offset; memcpy(&offset, arguments + 2, 8); uint64_t expect; memcpy(&expect, arguments + 4, 8); uint64_t update; memcpy(&update, arguments + 6, 8); +#ifdef AVIAN_HAS_CAS64 return atomicCompareAndSwap64 (&cast(target, offset), expect, update); +#else + ACQUIRE_FIELD_FOR_WRITE(t, local::fieldForOffset(t, target, offset)); + if (cast(target, offset) == expect) { + cast(target, offset) = update; + return true; + } else { + return false; + } +#endif } extern "C" JNIEXPORT int64_t JNICALL diff --git a/src/x86.h b/src/x86.h index 72570e03a8..72cc891ac1 100644 --- a/src/x86.h +++ b/src/x86.h @@ -255,6 +255,8 @@ atomicCompareAndSwap32(uint32_t* p, uint32_t old, uint32_t new_) #endif } +#define AVIAN_HAS_CAS64 + inline bool atomicCompareAndSwap64(uint64_t* p, uint64_t old, uint64_t new_) {