Index: loaders/src/org/openide/loaders/FolderChildren.java
===================================================================
RCS file: /cvs/openide/loaders/src/org/openide/loaders/FolderChildren.java,v
retrieving revision 1.5
diff -u -r1.5 FolderChildren.java
--- loaders/src/org/openide/loaders/FolderChildren.java 11 Jun 2004 08:34:21 -0000 1.5
+++ loaders/src/org/openide/loaders/FolderChildren.java 23 Sep 2004 13:45:58 -0000
@@ -175,34 +175,15 @@
}
return super.findChild(name);
}
-
+
+
+
/**
- * This code could probably be replaced by
- * Children.MUTEX.isReadAccess, if such call would be added to Mutex
* @return true if it is safe to wait (our thread is
* not in Children.MUTEX.readAccess
*/
private static boolean checkChildrenMutex() {
- class MutexChecker implements Runnable {
- public boolean checkReadOrWrite;
- public boolean inReadAccess = true;
- public boolean inWriteAccess = true;
- public void run () {
- if (checkReadOrWrite) {
- inReadAccess = false;
- } else {
- inWriteAccess = false;
- }
- }
- }
- MutexChecker test = new MutexChecker();
- // the code will run either immediatally or after we leave readAccess
- // section
- Children.MUTEX.postWriteRequest(test);
- test.checkReadOrWrite = true;
- Children.MUTEX.postReadRequest(test);
-
- return !test.inReadAccess && !test.inWriteAccess;
+ return !Children.MUTEX.isReadAccess() && !Children.MUTEX.isWriteAccess ();
}
/** Initializes the children.
Index: src/org/openide/nodes/Children.java
===================================================================
RCS file: /cvs/openide/src/org/openide/nodes/Children.java,v
retrieving revision 1.123
diff -u -r1.123 Children.java
--- src/org/openide/nodes/Children.java 7 Sep 2004 12:54:22 -0000 1.123
+++ src/org/openide/nodes/Children.java 23 Sep 2004 13:46:04 -0000
@@ -35,7 +35,7 @@
* access to children.
*/
static final Mutex.Privileged PR = new Mutex.Privileged ();
-
+
/** Lock for access to hierarchy of all node lists.
* Anyone who needs to ensure that there will not
* be shared accesses to hierarchy nodes can use this
@@ -473,21 +473,7 @@
} else {
// otherwise, if not initialize yet (arr.children) wait
// for the initialization to finish, but only if we can wait
-
- // we are not in ReadAccess
- // Children.MUTEX.isReadAccess, if such call would be added to Mutex
- class MutexChecker implements Runnable {
- public boolean inReadAccess = true;
- public void run () {
- inReadAccess = false;
- }
- }
- MutexChecker test = new MutexChecker();
- // the code will run either immediatally or after we leave readAccess
- // section
- Children.MUTEX.postWriteRequest(test);
-
- if (test.inReadAccess || initThread == Thread.currentThread()) {
+ if (MUTEX.isReadAccess() || initThread == Thread.currentThread()) {
// fail, we are in read access
if (cannotWorkBetter != null) {
cannotWorkBetter[0] = true;
Index: src/org/openide/util/Mutex.java
===================================================================
RCS file: /cvs/openide/src/org/openide/util/Mutex.java,v
retrieving revision 1.61
diff -u -r1.61 Mutex.java
--- src/org/openide/util/Mutex.java 8 Apr 2004 09:45:19 -0000 1.61
+++ src/org/openide/util/Mutex.java 23 Sep 2004 13:46:04 -0000
@@ -60,6 +60,11 @@
* } catch (MutexException ex) {
* throw (IOException)ex.getException ();
* }
+*
+* // check whether you are already in read access
+* if (m.isReadAccess ()) {
+* // do your work
+* }
*
*
* @author Ales Novak
@@ -67,6 +72,10 @@
public final class Mutex extends Object {
/** Mutex that allows code to be synchronized with the AWT event dispatch thread. */
public static final Mutex EVENT = new Mutex ();
+ /** this is used from tests to prevent upgrade from readAccess to writeAccess
+ * by strictly throwing exception. Otherwise we just notify that using ErrorManager.
+ */
+ static boolean beStrict;
// lock mode constants
/** Lock free */
@@ -328,6 +337,55 @@
}
}
+ /** Tests whether this thread has already entered the mutex in read access.
+ * If it returns true, calling readAccess
+ * will be executed immediatelly
+ * without any blocking.
+ * Calling postWriteAccess
will delay the execution
+ * of its Runnable
until a readAccess section is over
+ * and calling writeAccess
is strongly prohibited and will
+ * result in a warning as a deadlock prone behaviour.
+ *
+ * @return true if the thread is in read access section
+ * @since JST-PENDING
+ */
+ public boolean isReadAccess () {
+ Thread t = Thread.currentThread();
+ ThreadInfo info;
+
+ synchronized (LOCK) {
+ info = getThreadInfo(t);
+ if (info != null) {
+ if (info.counts[X] == 0) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /** Tests whether this thread has already entered the mutex in write access.
+ * If it returns true, calling writeAccess
will be executed
+ * immediatelly without any other blocking. postReadAccess
+ * will be delayed until a write access runnable is over.
+ *
+ * @return true if the thread is in write access section
+ * @since JST-PENDING
+ */
+ public boolean isWriteAccess () {
+ Thread t = Thread.currentThread();
+ ThreadInfo info;
+
+ synchronized (LOCK) {
+ info = getThreadInfo(t);
+ if (info != null) {
+ if (info.counts[S] == 0) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
/** Posts a read request. This request runs immediately iff
* this Mutex is in the shared mode or this Mutex is not contended
@@ -425,6 +483,14 @@
if (info.forced) {
info.forced = false;
} else {
+ if (requested == X && info.counts[S] > 0) {
+ IllegalStateException e = new IllegalStateException("WARNING: Going from readAccess to writeAccess, see #10778: http://www.netbeans.org/issues/show_bug.cgi?id=10778 "); // NOI18N
+ if (beStrict) {
+ throw e;
+ }
+ ErrorManager.getDefault().notify(e);
+ }
+
info.counts[requested]++;
if ((requested == S) && (info.counts[requested] == 1)) {
readersNo++;
@@ -432,7 +498,10 @@
}
return true;
} else if (canUpgrade(info.mode, requested)) { // S - X and no holders
- IllegalStateException e = new IllegalStateException("WARNING: Going from readAccess to writeAccess, see #10778: http://www.netbeans.org/issues/show_bug.cgi?id=10778 ");
+ IllegalStateException e = new IllegalStateException("WARNING: Going from readAccess to writeAccess, see #10778: http://www.netbeans.org/issues/show_bug.cgi?id=10778 "); // NOI18N
+ if (beStrict) {
+ throw e;
+ }
ErrorManager.getDefault().notify(e);
info.mode = X;
@@ -447,7 +516,10 @@
return true;
} else { // S - X and holders
- IllegalStateException e = new IllegalStateException("WARNING: Going from readAccess to writeAccess through queue, see #10778: http://www.netbeans.org/issues/show_bug.cgi?id=10778 ");
+ IllegalStateException e = new IllegalStateException("WARNING: Going from readAccess to writeAccess through queue, see #10778: http://www.netbeans.org/issues/show_bug.cgi?id=10778 "); // NOI18N
+ if (beStrict) {
+ throw e;
+ }
ErrorManager.getDefault().notify(e);
// chain follows
}
Index: test/unit/src/org/openide/util/MutexTest.java
===================================================================
RCS file: /cvs/openide/test/unit/src/org/openide/util/MutexTest.java,v
retrieving revision 1.9
diff -u -r1.9 MutexTest.java
--- test/unit/src/org/openide/util/MutexTest.java 19 Apr 2004 13:24:21 -0000 1.9
+++ test/unit/src/org/openide/util/MutexTest.java 23 Sep 2004 13:46:04 -0000
@@ -20,8 +20,8 @@
import org.netbeans.junit.*;
public class MutexTest extends NbTestCase {
- private Mutex.Privileged p;
- private Mutex m;
+ Mutex.Privileged p;
+ Mutex m;
@@ -44,6 +44,7 @@
protected void setUp () {
p = new Mutex.Privileged ();
m = new Mutex (p);
+ Mutex.beStrict = true;
}
public void testReadWriteRead() throws Exception {
@@ -529,6 +530,134 @@
p.enterWriteAccess();
p.exitWriteAccess();
}
+
+ public void testNoWayToDoReadAndThenWrite () {
+ class R implements Runnable {
+ public void run () {
+ m.writeAccess (this);
+ }
+ }
+
+ try {
+ m.readAccess (new R ());
+ fail ("This is supposed to throw an IllegalStateException");
+ } catch (IllegalStateException ex) {
+ // ok, this is expected
+ }
+ }
+
+ public void testNoWayToDoWriteThenReadAndThenWrite () {
+ class R implements Runnable {
+ public boolean second;
+ public boolean end;
+ public boolean ending;
+
+ public void run () {
+ if (end) {
+ ending = true;
+ return;
+ }
+
+ if (second) {
+ end = true;
+ m.writeAccess (this);
+ } else {
+ second = true;
+ m.readAccess (this);
+ }
+ }
+ }
+ R r = new R ();
+ try {
+ m.writeAccess (r);
+ fail ("This is supposed to throw an IllegalStateException");
+ } catch (IllegalStateException ex) {
+ // ok, this is expected
+ assertTrue ("We were in the write access section", r.second);
+ assertTrue ("We were before the writeAcess(this)", r.end);
+ assertFalse ("We never reached ending", r.ending);
+ }
+ }
+
+ public void testIsOrIsNotInReadOrWriteAccess () {
+ new ReadWriteChecking ("No r/w", Boolean.FALSE, Boolean.FALSE).run ();
+ m.readAccess (new ReadWriteChecking ("r but no w", Boolean.TRUE, Boolean.FALSE));
+ m.writeAccess (new ReadWriteChecking ("w but no r", Boolean.FALSE, Boolean.TRUE));
+ m.readAccess (new Runnable () {
+ public void run () {
+ m.postReadRequest (new ReadWriteChecking ("+r -w", Boolean.TRUE, Boolean.FALSE));
+ }
+ });
+ m.readAccess (new Runnable () {
+ public void run () {
+ m.postWriteRequest (new ReadWriteChecking ("-r +w", Boolean.FALSE, Boolean.TRUE));
+ }
+ });
+ m.writeAccess (new Runnable () {
+ public void run () {
+ m.postReadRequest (new ReadWriteChecking ("+r -w", Boolean.TRUE, Boolean.FALSE));
+ }
+ });
+ m.writeAccess (new Runnable () {
+ public void run () {
+ m.postWriteRequest (new ReadWriteChecking ("-r +w", Boolean.FALSE, Boolean.TRUE));
+ }
+ });
+
+ // write->read->test (downgrade from write to read)
+ m.writeAccess (new Runnable () {
+ public boolean second;
+
+ public void run () {
+ if (!second) {
+ second = true;
+ m.readAccess (this);
+ return;
+ }
+
+ class P implements Runnable {
+ public boolean exec;
+ public void run () {
+ exec = true;
+ }
+ }
+ P r = new P ();
+ P w = new P ();
+ m.postWriteRequest (w);
+ m.postReadRequest (r);
+ assertFalse ("Writer not executed", w.exec);
+ assertFalse ("Reader not executed", r.exec);
+
+ m.readAccess (new ReadWriteChecking ("+r no w", Boolean.FALSE, Boolean.FALSE));
+ }
+ });
+
+ new ReadWriteChecking ("None at the end", Boolean.FALSE, Boolean.FALSE).run ();
+ }
+
+ private class ReadWriteChecking implements Runnable {
+ public Boolean read;
+ public Boolean write;
+ public String msg;
+
+ public ReadWriteChecking (String msg, Boolean read, Boolean write) {
+ assertNotNull ("Msg cannot be null", msg);
+ this.msg = msg;
+ this.read = read;
+ this.write = write;
+ }
+
+ protected void finalize () {
+ assertNull ("Run method was not called!", msg);
+ }
+
+ public void run () {
+ if (write != null) assertEquals (msg, write.booleanValue (), m.isWriteAccess ());
+ if (read != null) assertEquals (msg, read.booleanValue (), m.isReadAccess ());
+ msg = null;
+ }
+ }
+
private static class State implements Runnable {
public int state;