diff --git a/java.source/src/org/netbeans/modules/java/source/indexing/JavaBinaryIndexer.java b/java.source/src/org/netbeans/modules/java/source/indexing/JavaBinaryIndexer.java --- a/java.source/src/org/netbeans/modules/java/source/indexing/JavaBinaryIndexer.java +++ b/java.source/src/org/netbeans/modules/java/source/indexing/JavaBinaryIndexer.java @@ -67,7 +67,7 @@ LOG.log(Level.FINE, "index({0})", context.getRootURI()); try { final ClassIndexManager cim = ClassIndexManager.getDefault(); - cim.writeLock(new ClassIndexManager.ExceptionAction() { + cim.reserveWriteLock(new ClassIndexManager.ExceptionAction() { public Void run() throws IOException, InterruptedException { CachingArchiveProvider.getDefault().clearArchive(context.getRootURI()); File cacheFolder = JavaIndex.getClassFolder(context.getRootURI()); diff --git a/java.source/src/org/netbeans/modules/java/source/indexing/JavaCustomIndexer.java b/java.source/src/org/netbeans/modules/java/source/indexing/JavaCustomIndexer.java --- a/java.source/src/org/netbeans/modules/java/source/indexing/JavaCustomIndexer.java +++ b/java.source/src/org/netbeans/modules/java/source/indexing/JavaCustomIndexer.java @@ -129,7 +129,7 @@ splitSources(files,javaSources), context.getRootURI()); - ClassIndexManager.getDefault().writeLock(new ClassIndexManager.ExceptionAction() { + ClassIndexManager.getDefault().reserveWriteLock(new ClassIndexManager.ExceptionAction() { public Void run() throws IOException, InterruptedException { return TaskCache.getDefault().refreshTransaction(new ExceptionAction() { public Void run() throws Exception { @@ -240,7 +240,7 @@ JavaIndex.LOG.fine("Ignoring request with no root"); return; } - ClassIndexManager.getDefault().writeLock(new ClassIndexManager.ExceptionAction() { + ClassIndexManager.getDefault().reserveWriteLock(new ClassIndexManager.ExceptionAction() { public Void run() throws IOException, InterruptedException { return TaskCache.getDefault().refreshTransaction(new ExceptionAction() { public Void run() throws Exception { diff --git a/java.source/src/org/netbeans/modules/java/source/usages/ClassIndexManager.java b/java.source/src/org/netbeans/modules/java/source/usages/ClassIndexManager.java --- a/java.source/src/org/netbeans/modules/java/source/usages/ClassIndexManager.java +++ b/java.source/src/org/netbeans/modules/java/source/usages/ClassIndexManager.java @@ -65,6 +65,7 @@ private static ClassIndexManager instance; private final Map instances = new HashMap (); private final ReentrantReadWriteLock lock; + private final InternalLock internalLock; private final List listeners = new CopyOnWriteArrayList (); private boolean invalid; private Set added; @@ -75,6 +76,7 @@ private ClassIndexManager() { this.lock = new ReentrantReadWriteLock (false); + this.internalLock = new InternalLock(); } public void addClassIndexManagerListener (final ClassIndexManagerListener listener) { @@ -86,19 +88,31 @@ assert listener != null; this.listeners.remove(listener); } - + + @Deprecated public T writeLock (final ExceptionAction r) throws IOException, InterruptedException { - this.lock.writeLock().lock(); - try { + //Ugly, in scala much more cleaner. + return reserveWriteLock( + new ExceptionAction() { + public T run() throws IOException, InterruptedException { + return takeWriteLock(r); + } + }); + } + + public T reserveWriteLock(final ExceptionAction r) throws IOException, InterruptedException { + synchronized (internalLock) { depth++; + if (depth == 1) { + this.added = new HashSet(); + this.removed = new HashSet(); + } + } + try { try { - if (depth == 1) { - this.added = new HashSet(); - this.removed = new HashSet(); - } - try { - return r.run(); - } finally { + return r.run(); + } finally { + synchronized (internalLock) { if (depth == 1) { if (!removed.isEmpty()) { fire (removed, OP_REMOVE); @@ -107,12 +121,21 @@ if (!added.isEmpty()) { fire (added, OP_ADD); added.clear(); - } + } } } - } finally { + } + } finally { + synchronized (internalLock) { depth--; - } + } + } + } + + public T takeWriteLock(final ExceptionAction r) throws IOException, InterruptedException { + this.lock.writeLock().lock(); + try { + return r.run(); } finally { this.lock.writeLock().unlock(); } @@ -131,56 +154,64 @@ return this.lock.isWriteLockedByCurrentThread(); } - public synchronized ClassIndexImpl getUsagesQuery (final URL root) { - assert root != null; - if (invalid) { - return null; - } - return this.instances.get (root); + public ClassIndexImpl getUsagesQuery (final URL root) { + synchronized (internalLock) { + assert root != null; + if (invalid) { + return null; + } + return this.instances.get (root); + } } - public synchronized ClassIndexImpl createUsagesQuery (final URL root, final boolean source) throws IOException { + public ClassIndexImpl createUsagesQuery (final URL root, final boolean source) throws IOException { assert root != null; - if (invalid) { - return null; - } - ClassIndexImpl qi = this.instances.get (root); - if (qi == null) { - qi = PersistentClassIndex.create (root, JavaIndex.getIndex(root), source);//XXX - this.instances.put(root,qi); - if (added != null) { - added.add (root); + synchronized (internalLock) { + if (invalid) { + return null; } + ClassIndexImpl qi = this.instances.get (root); + if (qi == null) { + qi = PersistentClassIndex.create (root, JavaIndex.getIndex(root), source);//XXX + this.instances.put(root,qi); + if (added != null) { + added.add (root); + } + } + else if (source && !qi.isSource()){ + //Wrongly set up freeform project, which is common for it, prefer source + qi.close (); + qi = PersistentClassIndex.create (root, JavaIndex.getIndex(root), source);//XXX + this.instances.put(root,qi); + if (added != null) { + added.add (root); + } + } + return qi; } - else if (source && !qi.isSource()){ - //Wrongly set up freeform project, which is common for it, prefer source - qi.close (); - qi = PersistentClassIndex.create (root, JavaIndex.getIndex(root), source);//XXX - this.instances.put(root,qi); - if (added != null) { - added.add (root); - } - } - return qi; } - synchronized void removeRoot (final URL root) throws IOException { - ClassIndexImpl ci = this.instances.remove(root); - if (ci != null) { - ci.close(); - if (removed != null) { - removed.add (root); + void removeRoot (final URL root) throws IOException { + synchronized (internalLock) { + ClassIndexImpl ci = this.instances.remove(root); + if (ci != null) { + ci.close(); + if (removed != null) { + removed.add (root); + } } } } - public synchronized void close () { - invalid = true; - for (ClassIndexImpl ci : instances.values()) { - try { - ci.close(); - } catch (IOException ioe) { - Exceptions.printStackTrace(ioe); + public void close () { + synchronized (internalLock) { + invalid = true; + for (ClassIndexImpl ci : instances.values()) { + try { + ci.close(); + } catch (IOException ioe) { + Exceptions.printStackTrace(ioe); + } } } } @@ -212,5 +243,10 @@ instance = new ClassIndexManager (); } return instance; - } + } + + private static final class InternalLock { + + private InternalLock(){} + } } diff --git a/java.source/src/org/netbeans/modules/java/source/usages/LuceneIndex.java b/java.source/src/org/netbeans/modules/java/source/usages/LuceneIndex.java --- a/java.source/src/org/netbeans/modules/java/source/usages/LuceneIndex.java +++ b/java.source/src/org/netbeans/modules/java/source/usages/LuceneIndex.java @@ -714,8 +714,22 @@ return false; } } + + public void store (final Map, Object[]> refs, final List> topLevels) throws IOException { + try { + ClassIndexManager.getDefault().takeWriteLock(new ClassIndexManager.ExceptionAction() { + public Void run() throws IOException, InterruptedException { + _store(refs, topLevels); + return null; + } + }); + } catch (InterruptedException ie) { + //Never happens, just declared. The _store never throws InterruptedException + Exceptions.printStackTrace(ie); + } + } - public void store (final Map, Object[]> refs, final List> topLevels) throws IOException { + private void _store (final Map, Object[]> refs, final List> topLevels) throws IOException { checkPreconditions(); assert ClassIndexManager.getDefault().holdsWriteLock(); this.rootPkgCache = null; @@ -739,7 +753,22 @@ storeData(refs, create, timeStamp); } + //Todo: probably unsed, the java source should be refactored and cleaned up. public void store(final Map, Object[]> refs, final Set> toDelete) throws IOException { + try { + ClassIndexManager.getDefault().takeWriteLock(new ClassIndexManager.ExceptionAction() { + public Void run() throws IOException, InterruptedException { + _store(refs, toDelete); + return null; + } + }); + } catch (InterruptedException ie) { + //Never happens, just declared. The _store never throws InterruptedException + Exceptions.printStackTrace(ie); + } + } + + private void _store(final Map, Object[]> refs, final Set> toDelete) throws IOException { checkPreconditions(); assert ClassIndexManager.getDefault().holdsWriteLock(); this.rootPkgCache = null;