This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

Bug 222006 - NPE at Mutex.leaveImpl: synchronization in Mutex must be broken
Summary: NPE at Mutex.leaveImpl: synchronization in Mutex must be broken
Status: RESOLVED FIXED
Alias: None
Product: platform
Classification: Unclassified
Component: -- Other -- (show other bugs)
Version: 7.3
Hardware: All All
: P3 normal (vote)
Assignee: Jaroslav Tulach
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2012-11-12 22:39 UTC by eduardomaitre
Modified: 2013-07-17 02:41 UTC (History)
2 users (show)

See Also:
Issue Type: DEFECT
Exception Reporter: 195025


Attachments
stacktrace (756 bytes, text/plain)
2012-11-12 22:39 UTC, eduardomaitre
Details

Note You need to log in before you can comment on or make changes to this bug.
Description eduardomaitre 2012-11-12 22:39:07 UTC
Build: NetBeans IDE Dev (Build 201211120001)
VM: Java HotSpot(TM) 64-Bit Server VM, 23.5-b02, Java(TM) SE Runtime Environment, 1.7.0_09-b05
OS: Windows 7

Stacktrace: 
java.lang.NullPointerException
   at org.openide.util.Mutex.leaveImpl(Mutex.java:860)
   at org.openide.util.Mutex.leave(Mutex.java:841)
   at org.openide.util.Mutex$Privileged.exitReadAccess(Mutex.java:1683)
   at org.openide.nodes.EntrySupportDefault.getNodes(EntrySupportDefault.java:132)
   at org.openide.nodes.EntrySupportDefault.getNodes(EntrySupportDefault.java:172)
   at org.openide.nodes.EntrySupportDefault.getNodesCount(EntrySupportDefault.java:176)
Comment 1 eduardomaitre 2012-11-12 22:39:08 UTC
Created attachment 127670 [details]
stacktrace
Comment 2 Jaroslav Tulach 2012-11-16 17:21:43 UTC
Enforcing synchronization with getters and setters:

# This patch file was generated by NetBeans IDE
# It uses platform neutral UTF-8 encoding and \n newlines.
--- Base (BASE)
+++ Locally Modified (Based On LOCAL)
@@ -173,10 +173,14 @@
         { true, false, false, false },{ true, false, false, true }
     };
 
-    /** granted mode */
+    /** granted mode 
+     * @GuardedBy("LOCK")
+     */
     private int grantedMode = NONE;
     
-    /** The mode the mutex was in before it started chaining */
+    /** The mode the mutex was in before it started chaining 
+     * @GuardedBy("LOCK")
+     */
     private int origMode;
 
     /** protects internal data structures */
@@ -185,15 +189,19 @@
     /** wrapper, if any */
     private final Executor wrapper;
 
-    /** threads that - owns or waits for this mutex */
-    private /*final*/ Map<Thread,ThreadInfo> registeredThreads;
+    /** threads that - owns or waits for this mutex 
+     * @GuardedBy("LOCK")
+     */
+    private Map<Thread,ThreadInfo> registeredThreads;
 
-    /** number of threads that holds S mode (readersNo == "count of threads in registeredThreads that holds S") */
-
-    // NOI18N
+    /** number of threads that holds S mode (readersNo == "count of threads in registeredThreads that holds S") 
+     * @GuardedBy("LOCK")
+     */
     private int readersNo = 0;
 
-    /** a queue of waiting threads for this mutex */
+    /** a queue of waiting threads for this mutex 
+     * @GuardedBy("LOCK")
+     */
     private List<QueueCell> waiters;
 
     /** identification of the mutex */
@@ -598,10 +606,10 @@
         StringBuilder sbuff = new StringBuilder(512);
 
         synchronized (LOCK) {
-            sbuff.append("threads: ").append(registeredThreads).append(newline); // NOI18N
+            sbuff.append("threads: ").append(getRegisteredThreads()).append(newline); // NOI18N
             sbuff.append("readersNo: ").append(readersNo).append(newline); // NOI18N
-            sbuff.append("waiters: ").append(waiters).append(newline); // NOI18N
-            sbuff.append("grantedMode: ").append(grantedMode).append(newline); // NOI18N
+            sbuff.append("waiters: ").append(getWaiters()).append(newline); // NOI18N
+            sbuff.append("grantedMode: ").append(getGrantedMode()).append(newline); // NOI18N
         }
 
         return sbuff.toString();
@@ -651,15 +659,15 @@
                 ThreadInfo info = getThreadInfo(t);
 
                 if (info != null) {
-                    if (grantedMode == NONE) {
+                    if (getGrantedMode() == NONE) {
                         // defensive
                         throw new IllegalStateException();
                     }
                     // reenters
                     // requested == S -> always succeeds
                     // info.mode == X -> always succeeds
-                    if (((info.mode == S) && (grantedMode == X)) ||
-                        ((info.mode == X) && (grantedMode == S))) {
+                    if (((info.mode == S) && (getGrantedMode() == X)) ||
+                        ((info.mode == X) && (getGrantedMode() == S))) {
                         // defensive
                         throw new IllegalStateException();
                     }
@@ -692,9 +700,9 @@
                         info.mode = X;
                         info.counts[requested]++;
                         info.rsnapshot = info.counts[S];
-                        if (grantedMode == S) {
+                        if (getGrantedMode() == S) {
                             setGrantedMode(X);
-                        } else if (grantedMode == X) {
+                        } else if (getGrantedMode() == X) {
                             // defensive
                             throw new IllegalStateException();
                         }
@@ -711,7 +719,7 @@
                 } else {
                     if (isCompatible(requested)) {
                         setGrantedMode(requested);
-                        registeredThreads.put(t,
+                        getRegisteredThreads().put(t,
                                               info = new ThreadInfo(t, requested));
                         if (requested == S) {
                             readersNo++;
@@ -748,7 +756,7 @@
     private boolean reenterImpl(Thread t, int mode) {
         // from leaveX -> grantedMode is NONE or S
         if (mode == S) {
-            if ((grantedMode != NONE) && (grantedMode != S)) {
+            if ((getGrantedMode() != NONE) && (getGrantedMode() != S)) {
                 throw new IllegalStateException(this.toString());
             }
 
@@ -759,10 +767,10 @@
 
         // assert (mode == X)
         ThreadInfo tinfo = getThreadInfo(t);
-        boolean chainFromLeaveX = ((grantedMode == CHAIN) && (tinfo != null) && (tinfo.counts[X] > 0));
+        boolean chainFromLeaveX = ((getGrantedMode() == CHAIN) && (tinfo != null) && (tinfo.counts[X] > 0));
 
         // process grantedMode == X or CHAIN from leaveX OR grantedMode == NONE from leaveS
-        if ((grantedMode == X) || (grantedMode == NONE) || chainFromLeaveX) {
+        if ((getGrantedMode() == X) || (getGrantedMode() == NONE) || chainFromLeaveX) {
             enter(mode, t, true);
 
             return false;
@@ -773,7 +781,7 @@
             }
 
             ThreadInfo info = new ThreadInfo(t, mode);
-            registeredThreads.put(t, info);
+            getRegisteredThreads().put(t, info);
 
             // prevent from grantedMode == NONE (another thread - leaveS)
             readersNo += 2;
@@ -812,8 +820,8 @@
                 if (readersNo == 0) { // seems I may enter
 
                     // no one has higher prio?
-                    if (waiters.get(0) == cell) {
-                        waiters.remove(0);
+                    if (getWaiters().get(0) == cell) {
+                        getWaiters().remove(0);
                         
                         setGrantedMode(mode);
 
@@ -835,13 +843,12 @@
     /** Leaves this mutex */
     final void leave(Thread t) {
         boolean log = LOG.isLoggable(Level.FINER);
-
-        if (log) doLog("Leaving {0}", grantedMode); // NOI18N
-
+        synchronized (LOCK) {
+            if (log) doLog("Leaving {0}", getGrantedMode()); // NOI18N
         leaveImpl(t);
-
-        if (log) doLog("Leaving exit: {0}", grantedMode); // NOI18N
+            if (log) doLog("Leaving exit: {0}", getGrantedMode()); // NOI18N
     }
+    }
 
     private void leaveImpl(Thread t) {
         ThreadInfo info;
@@ -851,7 +858,7 @@
         synchronized (LOCK) {
             info = getThreadInfo(t);
 
-            switch (grantedMode) {
+            switch (getGrantedMode()) {
             case NONE:
                 throw new IllegalStateException();
 
@@ -953,7 +960,7 @@
                 } else {
                     info.mode = NONE;
                     setGrantedMode(NONE);
-                    registeredThreads.remove(info.t);
+                    getRegisteredThreads().remove(info.t);
                 }
 
                 if (info.getRunnableCount(S) > 0) {
@@ -999,7 +1006,7 @@
         if (info.counts[S] == 0) {
             // remove the thread
             info.mode = NONE;
-            registeredThreads.remove(info.t);
+            getRegisteredThreads().remove(info.t);
 
             // downsize readersNo
             if (readersNo <= 0) {
@@ -1021,14 +1028,14 @@
                 wakeUpOthers();
             } else if (info.getRunnableCount(X) > 0) {
                 return X;
-            } else if ((grantedMode == CHAIN) && (readersNo == 1)) {
+            } else if ((getGrantedMode() == CHAIN) && (readersNo == 1)) {
                 // can be the mode advanced from CHAIN? Examine first item of waiters!
-                for (int i = 0; i < waiters.size(); i++) {
-                    QueueCell qc = waiters.get(i);
+                for (int i = 0; i < getWaiters().size(); i++) {
+                    QueueCell qc = getWaiters().get(i);
 
                     synchronized (qc) {
                         if (qc.isGotOut()) {
-                            waiters.remove(i--);
+                            getWaiters().remove(i--);
 
                             continue;
                         }
@@ -1042,13 +1049,13 @@
                                     throw new IllegalStateException();
                                 }
 
-                                if (waiters.size() == 1) {
+                                if (getWaiters().size() == 1) {
                                     setGrantedMode(X);
                                 }
                                  // else let CHAIN
 
                                 tinfo.mode = X;
-                                waiters.remove(i);
+                                getWaiters().remove(i);
                                 qc.wakeMeUp();
                             }
                         }
@@ -1084,21 +1091,21 @@
         //qc.timeout = timeout;
         qc.priority2 = priority;
 
-        final int size = waiters.size();
+        final int size = getWaiters().size();
 
         if (size == 0) {
-            waiters.add(qc);
+            getWaiters().add(qc);
         } else if (qc.getPriority() == Integer.MAX_VALUE) {
-            waiters.add(0, qc);
+            getWaiters().add(0, qc);
         } else {
             QueueCell cursor;
             int i = 0;
 
             do {
-                cursor = waiters.get(i);
+                cursor = getWaiters().get(i);
 
                 if (cursor.getPriority() < qc.getPriority()) {
-                    waiters.add(i, qc);
+                    getWaiters().add(i, qc);
 
                     break;
                 }
@@ -1107,7 +1114,7 @@
             } while (i < size);
 
             if (i == size) {
-                waiters.add(qc);
+                getWaiters().add(qc);
             }
         }
 
@@ -1116,28 +1123,28 @@
 
     /** Scans through waiters and wakes up them */
     private void wakeUpOthers() {
-        if ((grantedMode == X) || (grantedMode == CHAIN)) {
+        if ((getGrantedMode() == X) || (getGrantedMode() == CHAIN)) {
             // defensive
             throw new IllegalStateException();
         }
 
-        if (waiters.isEmpty()) {
+        if (getWaiters().isEmpty()) {
             return;
         }
 
-        for (int i = 0; i < waiters.size(); i++) {
-            QueueCell qc = waiters.get(i);
+        for (int i = 0; i < getWaiters().size(); i++) {
+            QueueCell qc = getWaiters().get(i);
 
             synchronized (qc) {
                 if (qc.isGotOut()) {
                     // bogus waiter
-                    waiters.remove(i--);
+                    getWaiters().remove(i--);
 
                     continue;
                 }
 
                 if (isCompatible(qc.mode)) { // woken S -> should I wake X? -> no
-                    waiters.remove(i--);
+                    getWaiters().remove(i--);
                     qc.wakeMeUp();
                     setGrantedMode(qc.mode);
 
@@ -1151,7 +1158,7 @@
                             readersNo++;
                         }
 
-                        registeredThreads.put(qc.t, ti);
+                        getRegisteredThreads().put(qc.t, ti);
                     }
                 } else {
                     setGrantedMode(CHAIN);
@@ -1164,25 +1171,25 @@
     }
 
     private void wakeUpReaders() {
-        assert (grantedMode == NONE) || (grantedMode == S);
+        assert (getGrantedMode() == NONE) || (getGrantedMode() == S);
 
-        if (waiters.isEmpty()) {
+        if (getWaiters().isEmpty()) {
             return;
         }
 
-        for (int i = 0; i < waiters.size(); i++) {
-            QueueCell qc = waiters.get(i);
+        for (int i = 0; i < getWaiters().size(); i++) {
+            QueueCell qc = getWaiters().get(i);
 
             synchronized (qc) {
                 if (qc.isGotOut()) {
                     // bogus waiter
-                    waiters.remove(i--);
+                    getWaiters().remove(i--);
 
                     continue;
                 }
 
                 if (qc.mode == S) { // readers only
-                    waiters.remove(i--);
+                    getWaiters().remove(i--);
                     qc.wakeMeUp();
                     setGrantedMode(S);
 
@@ -1192,7 +1199,7 @@
                         ThreadInfo ti = new ThreadInfo(qc.t, qc.mode);
                         ti.forced = true;
                         readersNo++;
-                        registeredThreads.put(qc.t, ti);
+                        getRegisteredThreads().put(qc.t, ti);
                     }
                 }
             }
@@ -1277,12 +1284,13 @@
     */
     private boolean isCompatible(int requested) {
         // allow next reader in even in chained mode, if it was read access before
-        if (requested == S && grantedMode == CHAIN && origMode == S) return true;
-        return cmatrix[requested][grantedMode];
+        if (requested == S && getGrantedMode() == CHAIN && getOrigMode() == S) return true;
+        return cmatrix[requested][getGrantedMode()];
     }
 
     private ThreadInfo getThreadInfo(Thread t) {
-        return registeredThreads.get(t);
+        assert Thread.holdsLock(LOCK);
+        return getRegisteredThreads().get(t);
     }
 
     private boolean canUpgrade(int threadGranted, int requested) {
@@ -1471,6 +1479,25 @@
         return ret;
     }
 
+    private int getOrigMode() {
+        Thread.holdsLock(LOCK);
+        return origMode;
+    }
+
+    private void setOrigMode(int origMode) {
+        Thread.holdsLock(LOCK);
+        this.origMode = origMode;
+    }
+
+    private Map<Thread,ThreadInfo> getRegisteredThreads() {
+        Thread.holdsLock(LOCK);
+        return registeredThreads;
+    }
+
+    private List<QueueCell> getWaiters() {
+        return waiters;
+    }
+
     // --------------------------------------------- END OF EVENT METHODS ------------------------------
 
     /** Action to be executed in a mutex without throwing any checked exceptions.
@@ -1689,9 +1716,14 @@
     }
 
     private void setGrantedMode(int mode) {
+        assert Thread.holdsLock(LOCK);
         if (grantedMode != CHAIN && mode == CHAIN) {
-            origMode = grantedMode;
+            setOrigMode(grantedMode);
         }
         grantedMode = mode;
     }
+    private int getGrantedMode() {
+        assert Thread.holdsLock(LOCK);
+        return grantedMode;
 }
+}
Comment 3 Ondrej Vrabec 2013-07-02 09:41:12 UTC
jardo, can you apply the patch, then?
Comment 4 Jaroslav Tulach 2013-07-09 13:14:40 UTC
ergonomics#737fe6774c90
Comment 5 Quality Engineering 2013-07-17 02:41:51 UTC
Integrated into 'main-silver', will be available in build *201307162300* on http://bits.netbeans.org/dev/nightly/ (upload may still be in progress)

Changeset: http://hg.netbeans.org/main-silver/rev/737fe6774c90
User: Jaroslav Tulach <jtulach@netbeans.org>
Log: #222006: Guard access to internal mutable variables via assert Thread.holdsLock