Merge -> trunk.
authorStefan Ring <stefan@complang.tuwien.ac.at>
Mon, 3 Mar 2008 21:29:29 +0000 (22:29 +0100)
committerStefan Ring <stefan@complang.tuwien.ac.at>
Mon, 3 Mar 2008 21:29:29 +0000 (22:29 +0100)
.hgignore
src/threads/native/lock.c
src/threads/native/threads.c
src/threads/native/threads.h
tests/threadInterrupt.java [deleted file]
tests/threads/threadInterrupt.java [new file with mode: 0644]
tests/threads/waitAndInterrupt.java [new file with mode: 0644]

index df61478144eba52905f8c47c5a5546971027ba15..5f82c8d894f922dc10f71217f4911d4ce91a8c54 100644 (file)
--- a/.hgignore
+++ b/.hgignore
@@ -6,6 +6,12 @@ syntax: glob
 *.lo
 *.a
 *.o
+*.aux
+*.ids
+*.idx
+*.log
+*.out
+*.toc
 Makefile
 Makefile.in
 TAGS
@@ -44,3 +50,4 @@ tests/regression/resolving/*.class
 tests/regression/resolving/classes1/*.class
 tests/regression/resolving/classes2/*.class
 tests/regression/resolving/classes3/*.class
+tests/threads/*.class
index b7cf82117e5fa26bb414a90e2212447241bc3623..6bcc20294e84070f1cf89bdbe2216ab28c22088e 100644 (file)
@@ -1268,7 +1268,7 @@ static void lock_record_remove_waiter(lock_record_t *lr, threadobject *thread)
 static bool lock_record_wait(threadobject *thread, lock_record_t *lr, s8 millis, s4 nanos)
 {
        s4   lockcount;
-       bool wasinterrupted;
+       bool wasinterrupted = false;
 
        DEBUGLOCKS(("[lock_record_wait  : lr=%p, t=%p, millis=%lld, nanos=%d]",
                                lr, thread, millis, nanos));
@@ -1290,7 +1290,7 @@ static bool lock_record_wait(threadobject *thread, lock_record_t *lr, s8 millis,
 
        /* wait until notified/interrupted/timed out */
 
-       wasinterrupted = threads_wait_with_timeout_relative(thread, millis, nanos);
+       threads_wait_with_timeout_relative(thread, millis, nanos);
 
        /* re-enter the monitor */
 
@@ -1304,6 +1304,17 @@ static bool lock_record_wait(threadobject *thread, lock_record_t *lr, s8 millis,
 
        lr->count = lockcount;
 
+       /* We can only be signaled OR interrupted, not both. If both flags
+          are set, reset only signaled and leave the thread in
+          interrupted state. Otherwise, clear both. */
+
+       if (!thread->signaled) {
+               wasinterrupted = thread->interrupted;
+               thread->interrupted = false;
+       }
+
+       thread->signaled = false;
+
        /* return if we have been interrupted */
 
        return wasinterrupted;
@@ -1401,10 +1412,10 @@ static void lock_record_notify(threadobject *t, lock_record_t *lr, bool one)
 
                waitingthread = w->thread;
 
-               /* If the thread was already signaled but hasn't removed
-                  itself from the list yet, just ignore it. */
+               /* We must skip threads which have already been notified or
+                  interrupted. They will remove themselves from the list. */
 
-               if (waitingthread->signaled == true)
+               if (waitingthread->signaled || waitingthread->interrupted)
                        continue;
 
                /* Enter the wait-mutex. */
@@ -1414,7 +1425,11 @@ static void lock_record_notify(threadobject *t, lock_record_t *lr, bool one)
                DEBUGLOCKS(("[lock_record_notify: lr=%p, t=%p, waitingthread=%p, sleeping=%d, one=%d]",
                                        lr, t, waitingthread, waitingthread->sleeping, one));
 
-               /* Signal the thread if it's sleeping. */
+               /* Signal the thread if it's sleeping. sleeping can be false
+                  when the waiting thread is blocked between giving up the
+                  monitor and entering the waitmutex. It will eventually
+                  observe that it's signaled and refrain from going to
+                  sleep. */
 
                if (waitingthread->sleeping)
                        pthread_cond_signal(&(waitingthread->waitcond));
index ce18dfecd3b753477bbe1b0edf5bf708f9a0a098..f63abbfd638435170b7fa4d294255dca04ac9a03 100644 (file)
@@ -2052,16 +2052,10 @@ static bool threads_current_time_is_earlier_than(const struct timespec *tv)
                           If both tv_sec and tv_nsec are zero, this function
                                           waits for an unlimited amount of time.
 
-   RETURN VALUE:
-      true.........if the wait has been interrupted,
-         false........if the wait was ended by notification or timeout
-
 *******************************************************************************/
 
-static bool threads_wait_with_timeout(threadobject *t, struct timespec *wakeupTime)
+static void threads_wait_with_timeout(threadobject *t, struct timespec *wakeupTime)
 {
-       bool wasinterrupted;
-
        /* acquire the waitmutex */
 
        pthread_mutex_lock(&t->waitmutex);
@@ -2096,21 +2090,11 @@ static bool threads_wait_with_timeout(threadobject *t, struct timespec *wakeupTi
                }
        }
 
-       /* check if we were interrupted */
-
-       wasinterrupted = t->interrupted;
-
-       /* reset all flags */
-
-       t->interrupted = false;
-       t->signaled    = false;
        t->sleeping    = false;
 
        /* release the waitmutex */
 
        pthread_mutex_unlock(&t->waitmutex);
-
-       return wasinterrupted;
 }
 
 
@@ -2124,13 +2108,9 @@ static bool threads_wait_with_timeout(threadobject *t, struct timespec *wakeupTi
          millis.......milliseconds to wait
          nanos........nanoseconds to wait
 
-   RETURN VALUE:
-      true.........if the wait has been interrupted,
-         false........if the wait was ended by notification or timeout
-
 *******************************************************************************/
 
-bool threads_wait_with_timeout_relative(threadobject *thread, s8 millis,
+void threads_wait_with_timeout_relative(threadobject *thread, s8 millis,
                                                                                s4 nanos)
 {
        struct timespec wakeupTime;
@@ -2141,7 +2121,7 @@ bool threads_wait_with_timeout_relative(threadobject *thread, s8 millis,
 
        /* wait */
 
-       return threads_wait_with_timeout(thread, &wakeupTime);
+       threads_wait_with_timeout(thread, &wakeupTime);
 }
 
 
@@ -2228,6 +2208,8 @@ bool threads_check_if_interrupted_and_reset(void)
 
        thread = THREADOBJECT;
 
+       pthread_mutex_lock(&thread->waitmutex);
+
        /* get interrupted flag */
 
        intr = thread->interrupted;
@@ -2236,6 +2218,8 @@ bool threads_check_if_interrupted_and_reset(void)
 
        thread->interrupted = false;
 
+       pthread_mutex_unlock(&thread->waitmutex);
+
        return intr;
 }
 
@@ -2274,7 +2258,9 @@ void threads_sleep(s8 millis, s4 nanos)
 
        threads_calc_absolute_time(&wakeupTime, millis, nanos);
 
-       wasinterrupted = threads_wait_with_timeout(thread, &wakeupTime);
+       threads_wait_with_timeout(thread, &wakeupTime);
+
+       wasinterrupted = threads_check_if_interrupted_and_reset();
 
        if (wasinterrupted)
                exceptions_throw_interruptedexception();
index ed523f65b5760cd664dff8bacf1d8781e5540faa..f02f38871e9b8a64c9961927611322fedd6654d3 100644 (file)
@@ -238,7 +238,7 @@ void threads_join_all_threads(void);
 
 void threads_sleep(s8 millis, s4 nanos);
 
-bool threads_wait_with_timeout_relative(threadobject *t, s8 millis, s4 nanos);
+void threads_wait_with_timeout_relative(threadobject *t, s8 millis, s4 nanos);
 
 void threads_thread_interrupt(threadobject *thread);
 bool threads_check_if_interrupted_and_reset(void);
diff --git a/tests/threadInterrupt.java b/tests/threadInterrupt.java
deleted file mode 100644 (file)
index ef41083..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-public class threadInterrupt {
-       public static class firstthread implements Runnable {
-               private threadInterrupt s;
-
-               public firstthread(threadInterrupt s_) {
-                       s = s_;
-               }
-               public void run() {
-                       try {
-                               synchronized (s.o1) {
-                                       System.out.println("first thread!");
-                                       Thread.sleep(500);
-                                       System.out.println("interrupting");
-                                       s.t2.interrupt();
-                                       System.out.println("leaving");
-                               }
-                       } catch (Exception e) {
-                               e.printStackTrace();
-                       }
-               }
-       }
-
-       public static class secondthread implements Runnable {
-               private threadInterrupt s;
-
-               public secondthread(threadInterrupt s_) {
-                       s = s_;
-               }
-               public void run() {
-                       try {
-                               Thread.sleep(250);
-                               synchronized (s.o1) {
-                                       System.out.println("second thread!");
-                                       s.o1.wait();
-                               }
-                       } catch (Exception e) {
-                               e.printStackTrace();
-                       }
-               }
-       }
-
-       public Object o1 = new Object();
-       public Thread t1 = null;
-       public Thread t2 = null;
-
-       public static void main(String args[]) {
-               System.out.println("should exit with java.lang.InterruptedException");
-               threadInterrupt s = new threadInterrupt();
-               firstthread r1 = new firstthread(s);
-               secondthread r2 = new secondthread(s);
-
-               s.t1 = new Thread(r1, "a");
-               s.t2 = new Thread(r2, "b");
-               s.t1.start();
-               s.t2.start();
-       }
-}
-
-/*
- * These are local overrides for various environment variables in Emacs.
- * Please do not remove this and leave it at the end of the file, where
- * Emacs will automagically detect them.
- * ---------------------------------------------------------------------
- * Local variables:
- * mode: java
- * indent-tabs-mode: t
- * c-basic-offset: 4
- * tab-width: 4
- * End:
- * vim:noexpandtab:sw=4:ts=4:
- */
diff --git a/tests/threads/threadInterrupt.java b/tests/threads/threadInterrupt.java
new file mode 100644 (file)
index 0000000..ef41083
--- /dev/null
@@ -0,0 +1,71 @@
+public class threadInterrupt {
+       public static class firstthread implements Runnable {
+               private threadInterrupt s;
+
+               public firstthread(threadInterrupt s_) {
+                       s = s_;
+               }
+               public void run() {
+                       try {
+                               synchronized (s.o1) {
+                                       System.out.println("first thread!");
+                                       Thread.sleep(500);
+                                       System.out.println("interrupting");
+                                       s.t2.interrupt();
+                                       System.out.println("leaving");
+                               }
+                       } catch (Exception e) {
+                               e.printStackTrace();
+                       }
+               }
+       }
+
+       public static class secondthread implements Runnable {
+               private threadInterrupt s;
+
+               public secondthread(threadInterrupt s_) {
+                       s = s_;
+               }
+               public void run() {
+                       try {
+                               Thread.sleep(250);
+                               synchronized (s.o1) {
+                                       System.out.println("second thread!");
+                                       s.o1.wait();
+                               }
+                       } catch (Exception e) {
+                               e.printStackTrace();
+                       }
+               }
+       }
+
+       public Object o1 = new Object();
+       public Thread t1 = null;
+       public Thread t2 = null;
+
+       public static void main(String args[]) {
+               System.out.println("should exit with java.lang.InterruptedException");
+               threadInterrupt s = new threadInterrupt();
+               firstthread r1 = new firstthread(s);
+               secondthread r2 = new secondthread(s);
+
+               s.t1 = new Thread(r1, "a");
+               s.t2 = new Thread(r2, "b");
+               s.t1.start();
+               s.t2.start();
+       }
+}
+
+/*
+ * These are local overrides for various environment variables in Emacs.
+ * Please do not remove this and leave it at the end of the file, where
+ * Emacs will automagically detect them.
+ * ---------------------------------------------------------------------
+ * Local variables:
+ * mode: java
+ * indent-tabs-mode: t
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ * vim:noexpandtab:sw=4:ts=4:
+ */
diff --git a/tests/threads/waitAndInterrupt.java b/tests/threads/waitAndInterrupt.java
new file mode 100644 (file)
index 0000000..2f68b75
--- /dev/null
@@ -0,0 +1,202 @@
+// This test grew a bit more elaborate than anticipated...
+// It verifies that the JVM handles properly the case of a thread being
+// interrupted and notified at the same time.
+
+public class waitAndInterrupt {
+       private class semaphore {
+               private int v;
+               public semaphore(int v) {
+                       this.v = v;
+               }
+               public synchronized void semwait() {
+                       while (v == 0)
+                               try {
+                                       wait();
+                               } catch (InterruptedException e) {
+                               }
+                       v--;
+               }
+               public synchronized void sempost() {
+                       if (v == 0)
+                               notify();
+                       v++;
+               }
+       }
+
+       public static class firstthread implements Runnable {
+               private waitAndInterrupt s;
+
+               public firstthread(waitAndInterrupt s_) {
+                       s = s_;
+               }
+               public void run() {
+                       boolean iAmFirst = Thread.currentThread() == s.t1;
+                       try {
+                               int i = 0;
+                               int count_not = 0;
+                               int count_int = 0;
+                               for (;;) {
+                                       if (iAmFirst) {
+                                               if (++i == 100) {
+                                                       i = 0;
+                                                       System.out.println(Thread.currentThread().getName() + " still running, notified " + Integer.toString(count_not) + ", interrupted " + Integer.toString(count_int));
+                                               }
+                                               synchronized (s) {
+                                                       s.sem1.sempost();
+                                                       try {
+                                                               while (!s.notified)
+                                                                       s.wait();
+                                                               try {
+                                                                       s.wait();
+                                                               } catch (InterruptedException e) {
+                                                                       s.notify(); // wake t2
+                                                               }
+                                                               count_not++;
+                                                       } catch (InterruptedException e) {
+                                                               count_int++;
+                                                       }
+                                               }
+
+                                               s.sem5.sempost();
+                                               s.sem8.semwait();
+                                       } else {
+                                               s.sem1.semwait();
+                                               if (++i == 100) {
+                                                       i = 0;
+                                                       System.out.println(Thread.currentThread().getName() + " still running");
+                                               }
+                                               synchronized (s) {
+                                                       s.sem2.sempost();
+                                                       try {
+                                                               while (!s.notified)
+                                                                       s.wait();
+                                                               s.notified = false;
+                                                               count_not++;
+                                                       } catch (InterruptedException e) {
+                                                               count_int++;
+                                                       }
+                                               }
+
+                                               s.sem6.sempost();
+                                       }
+                               }
+                       } catch (Exception e) {
+                               e.printStackTrace();
+                       }
+               }
+       }
+
+       public static class otherthread implements Runnable {
+               private waitAndInterrupt s;
+
+               public otherthread(waitAndInterrupt s_) {
+                       s = s_;
+               }
+               public void run() {
+                       boolean iAmFirst = Thread.currentThread() == s.t3;
+                       try {
+                               int i = 0;
+                               for (;;) {
+                                       if (iAmFirst) {
+                                               s.sem3.semwait();
+                                               if (++i == 100) {
+                                                       i = 0;
+                                                       System.out.println(Thread.currentThread().getName() + " still running");
+                                               }
+                                               synchronized (s) {
+                                                       s.sem4.sempost();
+                                               }
+                                               s.t1.interrupt();
+                                               s.sem5.semwait();
+                                       } else {
+                                               s.sem4.semwait();
+                                               if (++i == 100) {
+                                                       i = 0;
+                                                       System.out.println(Thread.currentThread().getName() + " still running");
+                                               }
+                                               synchronized (s) {
+                                                       if (s.notified)
+                                                               System.out.println("shouldn't happen (1)");
+                                                       s.notified = true;
+                                                       s.notify();
+                                               }
+                                               s.sem6.semwait();
+                                       }
+                                       s.sem7.sempost();
+                               }
+                       } catch (Exception e) {
+                               e.printStackTrace();
+                       }
+               }
+       }
+
+       public static class controlthread implements Runnable {
+               private waitAndInterrupt s;
+
+               public controlthread(waitAndInterrupt s_) {
+                       s = s_;
+               }
+               public void run() {
+                       try {
+                               for (;;) {
+                                       s.sem2.semwait();
+                                       synchronized (s) {
+                                       }
+                                       s.sem3.sempost();
+                                       s.sem7.semwait();
+                                       s.sem7.semwait();
+                                       s.sem8.sempost(); // wake first
+                               }
+                       } catch (Exception e) {
+                               e.printStackTrace();
+                       }
+               }
+       }
+
+       public Thread t1 = null;
+       public Thread t2 = null;
+       public Thread t3 = null;
+       public Thread t4 = null;
+       public semaphore sem1 = new semaphore(0);
+       public semaphore sem2 = new semaphore(0);
+       public semaphore sem3 = new semaphore(0);
+       public semaphore sem4 = new semaphore(0);
+       public semaphore sem5 = new semaphore(0);
+       public semaphore sem6 = new semaphore(0);
+       public semaphore sem7 = new semaphore(0);
+       public semaphore sem8 = new semaphore(0);
+       public boolean notified = false;
+
+       public static void main(String args[]) {
+               waitAndInterrupt s = new waitAndInterrupt();
+               firstthread r1 = new firstthread(s);
+               firstthread r2 = new firstthread(s);
+               otherthread r3 = new otherthread(s);
+               controlthread r5 = new controlthread(s);
+
+               s.t1 = new Thread(r1, "a");
+               s.t2 = new Thread(r2, "b");
+               s.t3 = new Thread(r3, "c");
+               s.t4 = new Thread(r3, "d");
+               Thread t5 = new Thread(r5, "e");
+               s.t1.start();
+               s.t2.start();
+               s.t3.start();
+               s.t4.start();
+               t5.start();
+       }
+}
+
+/*
+ * These are local overrides for various environment variables in Emacs.
+ * Please do not remove this and leave it at the end of the file, where
+ * Emacs will automagically detect them.
+ * ---------------------------------------------------------------------
+ * Local variables:
+ * mode: java
+ * indent-tabs-mode: t
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ * vim:noexpandtab:sw=4:ts=4:
+ */