[ 
https://issues.apache.org/jira/browse/NETBEANS-4696?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17189010#comment-17189010
 ] 

Jaroslav Tulach edited comment on NETBEANS-4696 at 9/2/20, 6:14 AM:
--------------------------------------------------------------------

The general rule to fight with deadlocks in an API is [never hold a lock when 
calling foreign code|http://wiki.apidesign.org/wiki/Deadlock]. However in this 
case it is not that easy to find out who's an API and who's the client. What 
can help is to topologically sort and layer the modules according to their 
runtime dependencies - the ones below are an API, the ones above are clients. 
Then a callback from below to above which holds a lock would be problematic. 
Such a callback is happening in the deadlock:
{code:java}
org.netbeans.modules.html.knockout.KOHtmlExtension.isCustomTag(KOHtmlExtension.java:302)

is being called by "lower" module:

org.netbeans.modules.html.editor.gsf.HtmlGSFParser$1.isCustomTag(HtmlGSFParser.java:109)

is being called by "lower" module:

org.netbeans.modules.html.editor.lib.api.SyntaxAnalyzerResult.doParseHtml(SyntaxAnalyzerResult.java:259)

is being called by "lower" module:

org.netbeans.modules.csl.navigation.ElementScanningTask$1.run(ElementScanningTask.java:149)

is being called by "lower" module:

org.netbeans.modules.parsing.impl.TaskProcessor.callUserTask {code}
Parsing API doesn't really hold a "lock", but it only runs in a single 
dedicated thread (which is a shared, spare resource too). Can we avoid that? 
Too hard, right [~tomas_zezula] & [~zezulato...@gmail.com] & [~tzezula]? This 
is a core of the indexing infrastructure. We don't want to touch it. Who else 
to blame?

Let's make a rule: Whoever registers an indexer, isn't allowed to make a call 
to modules "above" Parsing API.

With such rule, it is clear that {{KOHtmlExtension}} breaks it by calling into 
Project UI API . Take a look at [four deadlock 
conditions|http://wiki.apidesign.org/wiki/Deadlock_conditions] and select one 
to break the deadlock the simplest way. This would do it:
{code:java}
--- 
a/webcommon/javascript2.knockout/src/org/netbeans/modules/javascript2/knockout/index/KnockoutIndex.java
+++ 
b/webcommon/javascript2.knockout/src/org/netbeans/modules/javascript2/knockout/index/KnockoutIndex.java
@@ -27,6 +27,8 @@
 import java.util.Map;
 import java.util.WeakHashMap;
 import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 import java.util.logging.Logger;
 import org.netbeans.api.project.Project;
 import org.netbeans.api.project.ui.OpenProjects;
@@ -57,8 +59,8 @@
                 if (!areProjectsOpen) {
                     try {
                         // just be sure that the projects are open
-                        OpenProjects.getDefault().openProjects().get();
-                    } catch (InterruptedException | ExecutionException ex) {
+                        OpenProjects.getDefault().openProjects().get(10, 
TimeUnit.SECONDS);
+                    } catch (InterruptedException | ExecutionException | 
TimeoutException ex) {
                         Exceptions.printStackTrace(ex);
                     } finally {
                         areProjectsOpen = true; {code}
e.g. don't wait indefinitely. However given [Roman Svitanič's 
comment|https://github.com/emilianbold/netbeans-releases/commit/30092308c4fef#diff-b6844ad6d2c225bbb1c8d6c0287d6a6cR80]
 "just be sure that the projects are open" - I'd suggest to delete the whole 
{{areProjectsOpen}} check. This shouldn't be here at all. If the dependency of 
{{javascript2.knockout}} module on Project UI API can be removed, then even 
better!

This would be my recommended fix of the deadlock, unless it is known to cause 
troubles somewhere (in unit tests?):
{code:java}
webcommon/javascript2.knockout$ git diff . 
diff --git a/webcommon/javascript2.knockout/nbproject/project.xml 
b/webcommon/javascript2.knockout/nbproject/project.xml
index 3f6fae4f8de0..47a191a3496d 100644
--- a/webcommon/javascript2.knockout/nbproject/project.xml
+++ b/webcommon/javascript2.knockout/nbproject/project.xml
@@ -96,15 +96,6 @@
                         <specification-version>1.61</specification-version>
                     </run-dependency>
                 </dependency>
-                <dependency>
-                    
<code-name-base>org.netbeans.modules.projectuiapi.base</code-name-base>
-                    <build-prerequisite/>
-                    <compile-dependency/>
-                    <run-dependency>
-                        <release-version>1</release-version>
-                        <specification-version>1.78</specification-version>
-                    </run-dependency>
-                </dependency>
                 <dependency>
                     <code-name-base>org.openide.filesystems</code-name-base>
                     <build-prerequisite/>
diff --git 
a/webcommon/javascript2.knockout/src/org/netbeans/modules/javascript2/knockout/index/KnockoutIndex.java
 
b/webcommon/javascript2.knockout/src/org/netbeans/modules/javascript2/knockout/index/KnockoutIndex.java
index eec45ef5b673..42676c1e14d4 100644
--- 
a/webcommon/javascript2.knockout/src/org/netbeans/modules/javascript2/knockout/index/KnockoutIndex.java
+++ 
b/webcommon/javascript2.knockout/src/org/netbeans/modules/javascript2/knockout/index/KnockoutIndex.java
@@ -26,10 +26,7 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.WeakHashMap;
:...skipping...
diff --git a/webcommon/javascript2.knockout/nbproject/project.xml 
b/webcommon/javascript2.knockout/nbproject/project.xml
index 3f6fae4f8de0..47a191a3496d 100644
--- a/webcommon/javascript2.knockout/nbproject/project.xml
+++ b/webcommon/javascript2.knockout/nbproject/project.xml
@@ -96,15 +96,6 @@
                         <specification-version>1.61</specification-version>
                     </run-dependency>
                 </dependency>
-                <dependency>
-                    
<code-name-base>org.netbeans.modules.projectuiapi.base</code-name-base>
-                    <build-prerequisite/>
-                    <compile-dependency/>
-                    <run-dependency>
-                        <release-version>1</release-version>
-                        <specification-version>1.78</specification-version>
-                    </run-dependency>
-                </dependency>
                 <dependency>
                     <code-name-base>org.openide.filesystems</code-name-base>
                     <build-prerequisite/>
diff --git 
a/webcommon/javascript2.knockout/src/org/netbeans/modules/javascript2/knockout/index/KnockoutIndex.java
 
b/webcommon/javascript2.knockout/src/org/netbeans/modules/javascript2/knockout/index/KnockoutIndex.java
index eec45ef5b673..42676c1e14d4 100644
--- 
a/webcommon/javascript2.knockout/src/org/netbeans/modules/javascript2/knockout/index/KnockoutIndex.java
+++ 
b/webcommon/javascript2.knockout/src/org/netbeans/modules/javascript2/knockout/index/KnockoutIndex.java
@@ -26,10 +26,7 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.WeakHashMap;
-import java.util.concurrent.ExecutionException;
-import java.util.logging.Logger;
 import org.netbeans.api.project.Project;
-import org.netbeans.api.project.ui.OpenProjects;
 import org.netbeans.modules.parsing.spi.indexing.support.IndexResult;
 import org.netbeans.modules.parsing.spi.indexing.support.QuerySupport;
 import org.openide.filesystems.FileObject;
@@ -40,12 +37,8 @@ import org.openide.util.Exceptions;
  * @author Roman Svitanic
  */
 public class KnockoutIndex {
-
-    private static final Logger LOGGER = 
Logger.getLogger(KnockoutIndex.class.getSimpleName());
-
     private static final Map<Project, KnockoutIndex> INDEXES = new 
WeakHashMap<>();
     private final QuerySupport querySupport;
-    private static boolean areProjectsOpen = false;
 
     public static KnockoutIndex get(Project project) throws IOException {
         if (project == null) {
@@ -54,16 +47,6 @@ public class KnockoutIndex {
         synchronized (INDEXES) {
             KnockoutIndex index = INDEXES.get(project);
             if (index == null) {
-                if (!areProjectsOpen) {
-                    try {
-                        // just be sure that the projects are open
-                        OpenProjects.getDefault().openProjects().get();
-                    } catch (InterruptedException | ExecutionException ex) {
-                        Exceptions.printStackTrace(ex);
-                    } finally {
-                        areProjectsOpen = true;
-                    }
-                }
                 Collection<FileObject> sourceRoots = 
QuerySupport.findRoots(project,
                         null /* all source roots */,
                         Collections.<String>emptyList(),
 {code}


was (Author: jtulach):
The general rule to fight with deadlocks in an API is [never hold a lock when 
calling foreign code|http://wiki.apidesign.org/wiki/Deadlock]. However in this 
case it is not that easy to find out who's an API and who's the client. What 
can help is to topologically sort and layer the modules according to their 
runtime dependencies - the ones below are an API, the ones above are clients. 
Then a callback from below to above which holds a lock would be problematic. 
Such a callback is happening in the deadlock:
{code:java}
org.netbeans.modules.html.knockout.KOHtmlExtension.isCustomTag(KOHtmlExtension.java:302)

is being called by "lower" module:

org.netbeans.modules.html.editor.gsf.HtmlGSFParser$1.isCustomTag(HtmlGSFParser.java:109)

is being called by "lower" module:

org.netbeans.modules.html.editor.lib.api.SyntaxAnalyzerResult.doParseHtml(SyntaxAnalyzerResult.java:259)

is being called by "lower" module:

org.netbeans.modules.csl.navigation.ElementScanningTask$1.run(ElementScanningTask.java:149)

is being called by "lower" module:

org.netbeans.modules.parsing.impl.TaskProcessor.callUserTask {code}
Parsing API doesn't really hold a "lock", but it only runs in a single 
dedicated thread (which is a shared, spare resource too). Can we avoid that? 
Too hard, right [~tomas_zezula] & [~zezulato...@gmail.com] & [~tzezula]? This 
is a core of the indexing infrastructure. We don't want to touch it. Who else 
to blame?

Let's make a rule: Whoever registers an indexer, isn't allowed to make a call 
to modules "above" Parsing API.

With such rule, it is clear that {{KOHtmlExtension}} breaks it by calling into 
Project UI API . Take a look at [four deadlock 
conditions|http://wiki.apidesign.org/wiki/Deadlock_conditions] and select one 
to break the deadlock the simplest way. This would do it:
{code:java}
--- 
a/webcommon/javascript2.knockout/src/org/netbeans/modules/javascript2/knockout/index/KnockoutIndex.java
+++ 
b/webcommon/javascript2.knockout/src/org/netbeans/modules/javascript2/knockout/index/KnockoutIndex.java
@@ -27,6 +27,8 @@
 import java.util.Map;
 import java.util.WeakHashMap;
 import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 import java.util.logging.Logger;
 import org.netbeans.api.project.Project;
 import org.netbeans.api.project.ui.OpenProjects;
@@ -57,8 +59,8 @@
                 if (!areProjectsOpen) {
                     try {
                         // just be sure that the projects are open
-                        OpenProjects.getDefault().openProjects().get();
-                    } catch (InterruptedException | ExecutionException ex) {
+                        OpenProjects.getDefault().openProjects().get(10, 
TimeUnit.SECONDS);
+                    } catch (InterruptedException | ExecutionException | 
TimeoutException ex) {
                         Exceptions.printStackTrace(ex);
                     } finally {
                         areProjectsOpen = true; {code}
e.g. don't wait indefinitely. However given [Roman Svitanič's 
comment|https://github.com/emilianbold/netbeans-releases/commit/30092308c4fef#diff-b6844ad6d2c225bbb1c8d6c0287d6a6cR80]
 "just be sure that the projects are open" - I'd suggest to delete the whole 
{{areProjectsOpen}} check. This shouldn't be here at all. If the dependency of 
{{javascript2.knockout}} module on Project UI API can be removed, then even 
better!

> NetBeans 12.1-beta1 sometimes hangs on open
> -------------------------------------------
>
>                 Key: NETBEANS-4696
>                 URL: https://issues.apache.org/jira/browse/NETBEANS-4696
>             Project: NetBeans
>          Issue Type: Bug
>          Components: ide - Performance
>    Affects Versions: 12.1
>         Environment: Linux (Debian "Buster")
> OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.7+10)
>            Reporter: Glenn Holmer
>            Priority: Major
>         Attachments: 12.1-vc1-thread-dump-orac.txt, 
> nb12.1-beta1-thread-dump.png, nb12.1-beta1-thread-dump.txt, 
> netbeans-12.1-thread-dump.txt
>
>
>  
> NetBeans sometimes hangs on startup; the symptom is a stalled "Opening 
> Projects" progress bar. I haven't been able to find a repro case, but I'm 
> attaching a thread dump (from VisualVM) and screen shot.



--
This message was sent by Atlassian Jira
(v8.3.4#803005)

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@netbeans.apache.org
For additional commands, e-mail: commits-h...@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists

Reply via email to