seeking help with progress bar and threading issues

Short question:

from within the Swing dispatch event thread, how does one create another thread that has or can get permission to modify a Document or PsiFile?

Context:

I'm adding the ability to execute my plugin on a directory in the project view (right click on directory to run the plugin on all files in the directory). When I do that, the actionPerformed() method in the plugin is being called on the Swing dispatch event thread. I can create a progress bar, put it in a dialog, and display it.

But I don't want to do all the plugin work on that thread. All updating of files has to be done inside an Application.runWriteAction() call (or I get an exception), and this call has to be made by the Swing dispatch event thread. All updating of files also appears to have to be done inside a CommandProcessor.executeCommand() call so that I don't get an exception about trying to perform an undo-able operation outside that call. I've tried all permutations I can think of but (unless I simply do all the work on the Swing dispatch event thread) I usually get



Can someone suggest a workaround? These requirements (about being inside a writeAction and CommandProcessor.executeCommand()) seem to be new with build 957 or maybe the previous.

Thanks,
-Dave

12 comments

On further thought, a more basic question is: why is Application.runWriteAction() limited to being called by the Swing dispatch event thread? Is it really intended that only that thread can change files (documents)? If so, how does one manage to get a progress bar (or any other UI) to work while the Swing thread is busy working on the documents?

It would seem to me that any thread should be able to call Application.run{Read,Write}Action() methods, if their purpose is to serve as a Reader/Writer lock on Documents (allowing multiple readers or only one writer access).

Could someone from IntelliJ answer this, please?

Thanks,
-Dave

0

Could someone from JetBrains take a stab at this? Please? :)

Thanks,
-Dave

0

On further thought, a more basic question is: why is

Application.runWriteAction() limited to being called by the Swing dispatch
event thread? Is it really intended that only that thread can change files
(documents)?

Yes, it's an intentional limitation.

If so, how does one manage to get a progress bar (or any other UI) to work

while the Swing thread is busy working on the documents?

Do you mean that it does change the documents? Could you provide an example
of such activity?


--
Valentin Kipiatkov
JetBrains, Inc
http://www.intellij.com
"Develop with pleasure!"

"Dave Kriewall" <no_mail@jetbrains.com> wrote in message
news:359596.1067879276714.JavaMail.itn@is.intellij.net...

On further thought, a more basic question is: why is

Application.runWriteAction() limited to being called by the Swing dispatch
event thread? Is it really intended that only that thread can change files
(documents)? If so, how does one manage to get a progress bar (or any other
UI) to work while the Swing thread is busy working on the documents?
>

It would seem to me that any thread should be able to call

Application.run{Read,Write}Action() methods, if their purpose is to serve as
a Reader/Writer lock on Documents (allowing multiple readers or only one
writer access).
>

Could someone from IntelliJ answer this, please?

>

Thanks,
-Dave



0

Hi Valentin,

Thanks for your reply. I'll give you two examples of what I'm trying to do.

Example 1). I want to enhance a plugin (the Rearranger) so that it operates on entire directories instead of just the current editor's document. I've got code that will recursively find all the documents and edit them. But while this is in progress, I want to display a progress bar showing % complete, and allowing the user to Cancel the operation.

Example 2). I want to enhance the Tabifier plugin so that it can display progress while reformatting a document. (If the document is really long, it can take 15-30 seconds.)

Both of the plugins are being called on the Swing thread.

I wrote code for the progress bar but it was never being updated, because the Swing thread was being used to update the documents themselves; there was no "time" left over for asynchronously updating the progress bar. I tried forcing an immediate paint of the progress bar whenever it changes value; this works after a fashion, but the Cancel button is inoperative. (So is the Close dialog button.)

Swing guidelines say that we're not supposed to do long-term operations on the Swing thread, because the UI becomes unresponsive. That's why I was surprised that the Swing thread is the only one that can update documents. It seems like an unnecessary limitation in any case, because you have the Application.run{Read/Write}Action method which I assume act as a reader/writer lock, allowing any number of simultaneous readers, but only one writer (and no readers while the writer works).

In any case, let's say for argument's sake that the Swing thread is the only one that can change documents. Is there a technique you've developed for displaying a progress bar using that same Swing thread? I don't think you have such a progress bar, e.g. while doing Code Reformat of an entire directory, but perhaps I'm wrong. The only progress bars implemented in IDEA seem to not update documents; I'm thinking of the parsing of large .jar files, or finding all usages of an item. Presumably any thread can read a document, which leaves the Swing thread free to update the UI.

This also may explain some recent reports of being unable to cancel an operation that updates a document.

Thanks for your help!
-Dave

0

In both of your two examples you can first calculate an in-memory version of
reformatted text (read operation!) and do the actual changes in UI thread
(they should not take much time). In the second example you can modify
documents one by one using SwingUtilities.invokeAndWait.

--
Valentin Kipiatkov
JetBrains, Inc
http://www.intellij.com
"Develop with pleasure!"


"Dave Kriewall" <no_mail@jetbrains.com> wrote in message
news:29674965.1070443068610.JavaMail.javamailuser@localhost...

Hi Valentin,

>

Thanks for your reply. I'll give you two examples of what I'm trying to

do.
>

Example 1). I want to enhance a plugin (the Rearranger) so that it

operates on entire directories instead of just the current editor's
document. I've got code that will recursively find all the documents and
edit them. But while this is in progress, I want to display a progress bar
showing % complete, and allowing the user to Cancel the operation.
>

Example 2). I want to enhance the Tabifier plugin so that it can display

progress while reformatting a document. (If the document is really long, it
can take 15-30 seconds.)
>

Both of the plugins are being called on the Swing thread.

>

I wrote code for the progress bar but it was never being updated, because

the Swing thread was being used to update the documents themselves; there
was no "time" left over for asynchronously updating the progress bar. I
tried forcing an immediate paint of the progress bar whenever it changes
value; this works after a fashion, but the Cancel button is inoperative.
(So is the Close dialog button.)
>

Swing guidelines say that we're not supposed to do long-term operations on

the Swing thread, because the UI becomes unresponsive. That's why I was
surprised that the Swing thread is the only one that can update documents.
It seems like an unnecessary limitation in any case, because you have the
Application.run{Read/Write}Action method which I assume act as a
reader/writer lock, allowing any number of simultaneous readers, but only
one writer (and no readers while the writer works).
>

In any case, let's say for argument's sake that the Swing thread is the

only one that can change documents. Is there a technique you've developed
for displaying a progress bar using that same Swing thread? I don't think
you have such a progress bar, e.g. while doing Code Reformat of an entire
directory, but perhaps I'm wrong. The only progress bars implemented in
IDEA seem to not update documents; I'm thinking of the parsing of large .jar
files, or finding all usages of an item. Presumably any thread can read a
document, which leaves the Swing thread free to update the UI.
>

This also may explain some recent reports of being unable to cancel an

operation that updates a document.
>

Thanks for your help!
-Dave



0

Valentin Kipiatkov (JetBrains) wrote:

In both of your two examples you can first calculate an in-memory version of
reformatted text (read operation!) and do the actual changes in UI thread
(they should not take much time). In the second example you can modify
documents one by one using SwingUtilities.invokeAndWait.


You didn't explain why write actions are only allowed on the Swing
thread. Just curious...

Ciao,
Gordon

--
Gordon Tyler (Software Developer)
Quest Software <http://java.quest.com/>
260 King Street East, Toronto, Ontario M5A 4L5, Canada
Voice: 416-643-4846 | Fax: 416-594-1919

0


You didn't explain why write actions are only allowed on the Swing
thread. Just curious...


Synchronization issues? The same reason why swing is single threaded.

Carlos

0

You didn't explain why write actions are only allowed on the Swing
thread. Just curious...


There are a few reasons but the main is the following: if we would allow
writing in any thread we would be forced to surround any readings with
runReadAction(). This makes code bloated with such stuff. Now we only need
to use runReadAction if we need to read from outside event dispatch thread
(rare case in fact).

--
Valentin Kipiatkov
JetBrains, Inc
http://www.intellij.com
"Develop with pleasure!"


"Gordon Tyler" <gordon.tyler@quest.com> wrote in message
news:bql4fs$9gl$1@is.intellij.net...

Valentin Kipiatkov (JetBrains) wrote:

>

In both of your two examples you can first calculate an in-memory

version of

reformatted text (read operation!) and do the actual changes in UI

thread

(they should not take much time). In the second example you can modify
documents one by one using SwingUtilities.invokeAndWait.

>

You didn't explain why write actions are only allowed on the Swing
thread. Just curious...

>

Ciao,
Gordon

>

--
Gordon Tyler (Software Developer)
Quest Software <http://java.quest.com/>
260 King Street East, Toronto, Ontario M5A 4L5, Canada
Voice: 416-643-4846 | Fax: 416-594-1919

>


0

Valentin Kipiatkov (JetBrains) wrote:

There are a few reasons but the main is the following: if we would allow
writing in any thread we would be forced to surround any readings with
runReadAction(). This makes code bloated with such stuff. Now we only need
to use runReadAction if we need to read from outside event dispatch thread
(rare case in fact).


Ah, I see. Thanks for the insight.

Ciao,
Gordon

--
Gordon Tyler (Software Developer)
Quest Software <http://java.quest.com/>
260 King Street East, Toronto, Ontario M5A 4L5, Canada
Voice: 416-643-4846 | Fax: 416-594-1919

0

Thanks, Valentin. I didn't realize that non-Swing threads could use Application.runReadAction(), or that you do read actions on the Swing thread without going through runReadAction(). Given your information, I should be able to make progress.

May I ask one more question? By experimentation, I've found that I have to run my task inside

or I get an exception about trying to make a change outside an "undoable" environment (sorry, I don't recall the exact error, but I can get it for you.)

If I don't want or need the ability to undo the changes that my plugin makes, can this call be avoided somehow? (without getting an exception, I mean.)

Thanks,
-Dave

0

If I don't want or need the ability to undo the changes that my plugin

makes, can this call be avoided somehow? (without getting an exception, I
mean.)

The easiest way to avoid the exception is surrounding it with
CommandProcessor.executeCommand() :-). I don't imagine any simplier solution
right now.

--
Valentin Kipiatkov
JetBrains, Inc
http://www.intellij.com
"Develop with pleasure!"


"Dave Kriewall" <no_mail@jetbrains.com> wrote in message
news:24120348.1070768203547.JavaMail.javamailuser@localhost...

Thanks, Valentin. I didn't realize that non-Swing threads could use

Application.runReadAction(), or that you do read actions on the Swing thread
without going through runReadAction(). Given your information, I should be
able to make progress.
>

May I ask one more question? By experimentation, I've found that I have

to run my task inside


or I get an exception about trying to make a change outside an "undoable"

environment (sorry, I don't recall the exact error, but I can get it for
you.)
>

If I don't want or need the ability to undo the changes that my plugin

makes, can this call be avoided somehow? (without getting an exception, I
mean.)
>

Thanks,
-Dave



0

Valentin,

I'm still missing something. I changed my code so that the document read operations are done on one thread, and any document update operations are done inside SwingUtilities.invokeAndWait(). But it doesn't work, because the Swing dispatch thread is tied up waiting for the read operations to complete.

For example, let's say I want to write a JUnit test that calls my plugin code to update a document. The JUnit test is called on the Swing dispatch thread (SDT). The SDT spawns a worker thread to do the reformatting in-memory. This other thread posts UI update tasks to the SDT. However, the SDT cannot return from the JUnit test until the worker thread has finished doing its work (since I want the test to actually compare results to expected.) So the SDT is not handling any Swing operations while it is suspended in the JUnit test.

Similarly, if I am running the plugin in production (not JUnit test), the EditorWriteActionHandler.executeWriteAction() method is called on the SDT. Again, the SDT spawns a worker thread to do the read-only operations on the document, building an in-memory list of changes. As the worker thread progresses, it calls SwingUtilities.invokeAndWait() to update the progress bar. Again, the Swing thread is not available to update any UI, because it is waiting for the worker thread to finish.

I can't simply return early (without waiting for the worker thread to complete its work) from the executeWriteAction() method so that the SDT can go about its business, because the user could be in a position to update the document while another thread is working on an older version of the document.

Can you suggest a threading model that will work? It is as if I need to force the Swing dispatch thread back to doing Swing work without returning from the JUnit call or executeWriteAction() method. I don't know of any way to do that. I might just as well start another Swing dispatch thread (if that were possible), since the "real" one is tied up.

I'm sure you have reasons, but why is it necessary for the SDT to do all updating of documents? It seems to preclude updating the UI simultaneously, e.g. with a progress bar.

Thanks very much,
-Dave

0

Please sign in to leave a comment.