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 243690 - Parsing API sends SchedulerTask.cancel() before ParserResultTask.run() and before Parser.getResult()
Summary: Parsing API sends SchedulerTask.cancel() before ParserResultTask.run() and be...
Status: RESOLVED FIXED
Alias: None
Product: editor
Classification: Unclassified
Component: Parsing & Indexing (show other bugs)
Version: 8.0.1
Hardware: PC Solaris
: P2 normal (vote)
Assignee: Tomas Zezula
URL:
Keywords:
Depends on: 244142
Blocks: 243577
  Show dependency tree
 
Reported: 2014-04-09 09:17 UTC by Alexander Simon
Modified: 2014-06-02 16:27 UTC (History)
1 user (show)

See Also:
Issue Type: DEFECT
Exception Reporter:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Alexander Simon 2014-04-09 09:17:34 UTC
As result the "Editor Parsing Loop" thread is frozen.
It is a critical issue for C/C++ because semantic highlighting task can take a lot of time (for example 60 seconds).
Log of semantic task:

FINE [org.netbeans.modules.cnd.model.tasks]: SemanticHighlighter canceled in org.netbeans.modules.parsing.spi.Scheduler, Task=2,057,733,112
FINE [org.netbeans.modules.cnd.model.tasks]: getResult for org.netbeans.modules.cnd.highlight.semantic.SemanticHighlighter, Task=2,057,733,112, Result=2,131,249,536
FINE [org.netbeans.modules.cnd.model.tasks]: SemanticHighlighter started, Task=2,057,733,112, Result=2,131,249,536
FINE [org.netbeans.modules.cnd.model.tasks]: SemanticHighlighter finished for 387,843ms

"Editor Parsing Loop" blocked for 7 minutes.
Comment 1 Tomas Zezula 2014-04-09 12:07:31 UTC
This is pretty OK.
The Task.cancel can be called before Task.run as there is a inconsistency window among the moment when the task is dispatched, source parsed and executed.

The task should look like:

class Task {
    
    private final  AtomicBoolean cancel = new AtomicBoolean();
    
    void run() {
        canceled.set(false);
        ...
        if (canceled.get()) {
            return;
        }
        ....
    }

    void cancel() {
        canceled.set(true);
    }
}

Or did I miss something?
Comment 2 Alexander Simon 2014-04-09 13:43:38 UTC
(In reply to Tomas Zezula from comment #1)
> This is pretty OK.
> The Task.cancel can be called before Task.run as there is a inconsistency
> window among the moment when the task is dispatched, source parsed and
> executed.
> 
> The task should look like:
> 
> class Task {
>     
>     private final  AtomicBoolean cancel = new AtomicBoolean();
>     
>     void run() {
>         canceled.set(false);
>         ...
>         if (canceled.get()) {
>             return;
>         }
>         ....
>     }
> 
>     void cancel() {
>         canceled.set(true);
>     }
> }
> 
> Or did I miss something?
It is exactly what CND use.
Problem is in
> void run() {
>         canceled.set(false);
because the task was canceled before run and cancel is lost.
Comment 3 Tomas Zezula 2014-04-09 13:45:01 UTC
OK, thanks!
Now I got it.
Comment 4 Tomas Zezula 2014-05-02 07:26:47 UTC
Fixed jet-main 39ef7c3d6e30.
You need to update your ParserResultTasks to use CancelSupport.


public class MyTask extends ParserResultTask {
    private final CancelSupport cancel = CancelSupport.create(this);
    public void run (T result, SchedulerEvent event) {
       ...
       if (cancel.isCancelled()) {
           return;
       }
       ...
   }
}

Remove the cancel() method and AtomicBoolean used in it from your task.
Comment 5 Alexander Simon 2014-05-13 11:24:14 UTC
(In reply to Tomas Zezula from comment #4)
> Remove the cancel() method and AtomicBoolean used in it from your task.
Sorry, I do not understand how "Remove the cancel() method" can be done. Could you clarify.
Comment 6 Alexander Simon 2014-05-13 12:42:53 UTC
(In reply to Alexander Simon from comment #5)
> (In reply to Tomas Zezula from comment #4)
> > Remove the cancel() method and AtomicBoolean used in it from your task.
> Sorry, I do not understand how "Remove the cancel() method" can be done.
> Could you clarify.
CND uses following pattern:

public class NavigatorNodeFactoryTask extends IndexingAwareParserResultTask<Parser.Result> {
    private final CancelSupport cancel = CancelSupport.create(this);
    private AtomicBoolean canceled = new AtomicBoolean(false);
    
    public void run(Parser.Result result, SchedulerEvent event) {
        synchronized (this) {
            canceled.set(true);
            canceled = new AtomicBoolean(false);
        }
        if (cancel.isCancelled()) {
            return;
        }
        ....
	process(canceled);
    }

    @Override
    public final synchronized void cancel() {
        synchronized(this) {
            canceled.set(true);
        }
    }

    void process(AtomicBoolean canceled) {
        ....
        if (!canceled.get()) {
	    return;
        }
        ....
    }
}
Comment 7 Tomas Zezula 2014-05-13 12:56:39 UTC
Hi Alex.
The newly introduced CancelSupport is a thread safe replacement of the AtomicBoolean used in tasks.
So here is the sketch of your task using CancelSupport:

public class NavigatorNodeFactoryTask extends IndexingAwareParserResultTask<Parser.Result> {
    private final CancelSupport cancel = CancelSupport.create(this);
    
    public void run(Parser.Result result, SchedulerEvent event) {
        if (cancel.isCancelled()) {
            return;
        }
        ....
	process();
    }

    void process() {
        ....
        if (cancel.isCancelled()) {
	    return;
        }
        ....
    }
}

You can also look at parsing.api/test/unit/src//org/netbeans/modules/parsing/impl/CancelSupportTest.java.
Comment 8 Alexander Simon 2014-05-13 13:37:08 UTC
(In reply to Tomas Zezula from comment #7)
> Hi Alex.
> The newly introduced CancelSupport is a thread safe replacement of the
> AtomicBoolean used in tasks.
> So here is the sketch of your task using CancelSupport:
> 
> public class NavigatorNodeFactoryTask extends
> IndexingAwareParserResultTask<Parser.Result> {
>     private final CancelSupport cancel = CancelSupport.create(this);
>     
>     public void run(Parser.Result result, SchedulerEvent event) {
>         if (cancel.isCancelled()) {
>             return;
>         }
>         ....
> 	process();
>     }
> 
>     void process() {
>         ....
>         if (cancel.isCancelled()) {
> 	    return;
>         }
>         ....
>     }
> }
> 
> You can also look at
> parsing.api/test/unit/src//org/netbeans/modules/parsing/impl/
> CancelSupportTest.java.
Tomas, thank you.
This pattern work for synchronous task (task that performs work in the current thread) and requires a lot of changes in clients because canceler is passed in too many methods recursively.
I do not sure that the pattern will work for asynchronous (and synchronous-asynchronous) task (task that performs work in request processor).
Comment 9 Alexander Simon 2014-05-13 13:42:42 UTC
Tomas, I checked the fix. It works and fixed a problem.
My changes in the bug #244464.
Comment 10 Tomas Zezula 2014-05-13 14:49:10 UTC
For a task spawning a subtask it will work if the subtask ends before the task,
something like:

class MyParserResultTask {
     void run(result, event) {
         Future<?> subtask = fork();
         ...
         ...
         subtask.get();
     }
}

In case the subtask escapes from the task the call to CancelSupport.isCancelled() is undefined as a different task is executed.
However it's correct as the task already finished and it cannot be cancelled anymore.
Maybe something like this may help:

class MyParserResultTask {
     private final CancelSupport cancel = CancelSupport.create(this);
     void run(result, event) {
         final AtomicBoolean signal = new AtomicBoolean();
         fork(()->{... if (signal.get()) {return;} ...});
         ...
         if (cancel.isCancelled()) {
             //Tell subtask that we were cancelled.
             signal.set(true);
             return;
         }
         ...
         //Should be at the end of the task as well but
         //it's impossible to cancel the subtask after end of task.run() anyway.
         if (cancel.isCancelled()) {
             //Tell subtask that we were cancelled.
             signal.set(true);
         }
     }
}
Comment 11 petrk 2014-05-29 14:01:42 UTC
We (CND team) will integrate fix from 244142 into releases
Comment 12 Tomas Zezula 2014-05-29 17:21:35 UTC
OK.
If I should transplant the fix into some patch branch please let me know.