Re: RFR: 8327137: Add test for ConcurrentModificationException in BasicDirectoryModel [v3]

2024-04-01 Thread Tejesh R
On Fri, 15 Mar 2024 14:45:36 GMT, Alexey Ivanov  wrote:

>> I'm adding a regression test for 
>> [JDK-8323670](https://bugs.openjdk.org/browse/JDK-8323670) and 
>> [JDK-8307091](https://bugs.openjdk.org/browse/JDK-8307091); it's also a 
>> regression test for 
>> [JDK-8240690](https://bugs.openjdk.org/browse/JDK-8240690).
>> 
>> I referenced this test in PR #17462 in [this 
>> comment](https://github.com/openjdk/jdk/pull/17462#issuecomment-1914844026). 
>> I fine-tuned the test since that time.
>> 
>> The test doesn't fail all the time without the fix for JDK-8323670, however, 
>> it fails sometimes. If you run the test several times, it will likely fail 
>> _without the fix_.
>> 
>> For me, the test fails about 10 times from 40 runs in the CI. It fails on 
>> macOS more frequently than on Linux.
>> 
>> When the test passes, it usually completes in 5 minutes.
>> 
>> **How the test works**
>> 
>> The test creates a temporary directory in the current directory and creates 
>> a number of files in it. (The number of files is controlled by 
>> `NUMBER_OF_THREADS` constant). Then the test creates `JFileChooser` in the 
>> temporary directory.
>> 
>> The test starts several _scanner_ threads, the number is controlled by 
>> `NUMBER_OF_THREADS`, which repeatedly call 
>> `fileChooser.rescanCurrentDirectory()`. This results in calling 
>> `BasicDirectoryModel.validateFileCache` which starts a background thread — 
>> "Basic L&F File Loading Thread" — to enumerate the files.
>> 
>> A timer is used to create new files in the directory that the file chooser 
>> is using.
>> 
>> After enumerating the files, the File Loading Thread posts an event to EDT. 
>> The event updates `fileCache` and fires a `ListDataEvent`.
>> 
>> If the File Loading Thread is iterating over `fileCache` using `Iterator` 
>> (when `fileCache.subList` or `fileCache.equals` is running; or a new 
>> `Vector` instance is created from a `fileCache` or its sublist) and 
>> `fileCache` is being updated on EDT, then `ConcurrentModificationException` 
>> is thrown.
>> 
>> On Linux and on _headless_ macOS, `ShellFolder.invoke` is executed in the 
>> caller, which makes it easier to reproduce the issue. Because of 
>> [JDK-8325179](https://bugs.openjdk.org/browse/JDK-8325179), there are 
>> several File Loading Threads, which also helps to reproduce the issue.
>> 
>> On _headful_ macOS, the `BasicDirectoryModel` is not used, so the test does 
>> not reproduce the issue.
>> 
>> On Windows, the test does not fail or fails with `OutOfMemoryError`. It is 
>> because all the File Loading Threads are serialised on the COM thread, 
>> `ShellFolder.invoke` submits the task to the COM thread and waits for i...
>
> Alexey Ivanov has updated the pull request incrementally with one additional 
> commit since the last revision:
> 
>   Suppress throwing exceptions while deleting files

Looks good to me.

-

Marked as reviewed by tr (Committer).

PR Review: https://git.openjdk.org/jdk/pull/18109#pullrequestreview-1972734171


Re: RFR: 8327137: Add test for ConcurrentModificationException in BasicDirectoryModel [v3]

2024-03-31 Thread Sergey Bylokhov
On Fri, 29 Mar 2024 20:02:53 GMT, Alexey Ivanov  wrote:

>>> However, there are cases where instantiating and testing is safe on main 
>>> thread.
>> 
>> That is my point, make it less safe - when the component is created on one 
>> thread:EDT and then for some reason accessed on a different thread, the 
>> rescanCurrentDirectory should still work.
>
>> > However, there are cases where instantiating and testing is safe on main 
>> > thread.
>> 
>> That is my point, make it less safe - when the component is created on one 
>> thread:EDT and then for some reason accessed on a different thread, the 
>> rescanCurrentDirectory should still work.
> 
> I got your idea!
> 
> However, I still see no benefit for doing it.
> 
> Yes, when `JFileChooser` is instantiated, it creates its UI delegates and the 
> default model, `BasicDirectoryModel`. The constructor of the model calls 
> `validateFileCache` which starts the background thread and will update 
> `fileCache`.
> 
> It does not make the initialisation less thread-safe: the same two threads 
> are involved — `FilesLoader` and EDT. Yet there's no race yet…
> 
> The `ConcurrentModificationException` is thrown when the background 
> `FilesLoader` is iterating over `fileCache` while EDT runs 
> `DoChangeContents.run` which adds elements to `fileCache` or removes elements 
> from it.
> 
> By the time the `Scanner` threads start, a race becomes possible. If the 
> initial `FilesLoader` completes and posts the `DoChangeContents` object to 
> EDT, the `FilesLoader` threads created by the scanners will race… but 
> `fileCache` is empty initially, thus iterating over it is very quick.
> 
> Therefore getting `ConcurrentModificationException` is unlikely until 
> `fileCache` contains a list of files. This state is reached later in the 
> timeline of the test.
> 
> The initial `FilesLoader` thread that's started when `JFileChooser` is 
> instantiated plays a role too… Yet it's running concurrently with the 
> `Scanner` threads whether the `JFileChooser` object is created on the current 
> thread or on EDT.
> 
> ---
> 
> Taking the above into account, instantiating `JFileChooser` on EDT adds 
> complexity to the test but brings no benefits.

ok, I agree with you.

-

PR Review Comment: https://git.openjdk.org/jdk/pull/18109#discussion_r1545858033


Re: RFR: 8327137: Add test for ConcurrentModificationException in BasicDirectoryModel [v3]

2024-03-31 Thread Sergey Bylokhov
On Fri, 15 Mar 2024 14:45:36 GMT, Alexey Ivanov  wrote:

>> I'm adding a regression test for 
>> [JDK-8323670](https://bugs.openjdk.org/browse/JDK-8323670) and 
>> [JDK-8307091](https://bugs.openjdk.org/browse/JDK-8307091); it's also a 
>> regression test for 
>> [JDK-8240690](https://bugs.openjdk.org/browse/JDK-8240690).
>> 
>> I referenced this test in PR #17462 in [this 
>> comment](https://github.com/openjdk/jdk/pull/17462#issuecomment-1914844026). 
>> I fine-tuned the test since that time.
>> 
>> The test doesn't fail all the time without the fix for JDK-8323670, however, 
>> it fails sometimes. If you run the test several times, it will likely fail 
>> _without the fix_.
>> 
>> For me, the test fails about 10 times from 40 runs in the CI. It fails on 
>> macOS more frequently than on Linux.
>> 
>> When the test passes, it usually completes in 5 minutes.
>> 
>> **How the test works**
>> 
>> The test creates a temporary directory in the current directory and creates 
>> a number of files in it. (The number of files is controlled by 
>> `NUMBER_OF_THREADS` constant). Then the test creates `JFileChooser` in the 
>> temporary directory.
>> 
>> The test starts several _scanner_ threads, the number is controlled by 
>> `NUMBER_OF_THREADS`, which repeatedly call 
>> `fileChooser.rescanCurrentDirectory()`. This results in calling 
>> `BasicDirectoryModel.validateFileCache` which starts a background thread — 
>> "Basic L&F File Loading Thread" — to enumerate the files.
>> 
>> A timer is used to create new files in the directory that the file chooser 
>> is using.
>> 
>> After enumerating the files, the File Loading Thread posts an event to EDT. 
>> The event updates `fileCache` and fires a `ListDataEvent`.
>> 
>> If the File Loading Thread is iterating over `fileCache` using `Iterator` 
>> (when `fileCache.subList` or `fileCache.equals` is running; or a new 
>> `Vector` instance is created from a `fileCache` or its sublist) and 
>> `fileCache` is being updated on EDT, then `ConcurrentModificationException` 
>> is thrown.
>> 
>> On Linux and on _headless_ macOS, `ShellFolder.invoke` is executed in the 
>> caller, which makes it easier to reproduce the issue. Because of 
>> [JDK-8325179](https://bugs.openjdk.org/browse/JDK-8325179), there are 
>> several File Loading Threads, which also helps to reproduce the issue.
>> 
>> On _headful_ macOS, the `BasicDirectoryModel` is not used, so the test does 
>> not reproduce the issue.
>> 
>> On Windows, the test does not fail or fails with `OutOfMemoryError`. It is 
>> because all the File Loading Threads are serialised on the COM thread, 
>> `ShellFolder.invoke` submits the task to the COM thread and waits for i...
>
> Alexey Ivanov has updated the pull request incrementally with one additional 
> commit since the last revision:
> 
>   Suppress throwing exceptions while deleting files

Marked as reviewed by serb (Reviewer).

-

PR Review: https://git.openjdk.org/jdk/pull/18109#pullrequestreview-1970499628


Re: RFR: 8327137: Add test for ConcurrentModificationException in BasicDirectoryModel [v3]

2024-03-29 Thread Alexey Ivanov
On Fri, 29 Mar 2024 02:00:18 GMT, Sergey Bylokhov  wrote:

> > However, there are cases where instantiating and testing is safe on main 
> > thread.
> 
> That is my point, make it less safe - when the component is created on one 
> thread:EDT and then for some reason accessed on a different thread, the 
> rescanCurrentDirectory should still work.

I got your idea!

However, I still see no benefit for doing it.

Yes, when `JFileChooser` is instantiated, it creates its UI delegates and the 
default model, `BasicDirectoryModel`. The constructor of the model calls 
`validateFileCache` which starts the background thread and will update 
`fileCache`.

It does not make the initialisation less thread-safe: the same two threads are 
involved — `FilesLoader` and EDT. Yet there's no race yet…

The `ConcurrentModificationException` is thrown when the background 
`FilesLoader` is iterating over `fileCache` while EDT runs 
`DoChangeContents.run` which adds elements to `fileCache` or removes elements 
from it.

By the time the `Scanner` threads start, a race becomes possible. If the 
initial `FilesLoader` completes and posts the `DoChangeContents` object to EDT, 
the `FilesLoader` threads created by the scanners will race… but `fileCache` is 
empty initially, thus iterating over it is very quick.

Therefore getting `ConcurrentModificationException` is unlikely until 
`fileCache` contains a list of files. This state is reached later in the 
timeline of the test.

The initial `FilesLoader` thread that's started when `JFileChooser` is 
instantiated plays a role too… Yet it's running concurrently with the `Scanner` 
threads whether the `JFileChooser` object is created on the current thread or 
on EDT.

---

Taking the above into account, instantiating `JFileChooser` on EDT adds 
complexity to the test but brings no benefits.

-

PR Review Comment: https://git.openjdk.org/jdk/pull/18109#discussion_r1544825191


Re: RFR: 8327137: Add test for ConcurrentModificationException in BasicDirectoryModel [v3]

2024-03-28 Thread Sergey Bylokhov
On Wed, 27 Mar 2024 20:19:41 GMT, Alexey Ivanov  wrote:

> However, there are cases where instantiating and testing is safe on main 
> thread.

That is my point, make it less safe - when the component is created on one 
thread:EDT and then for some reason accessed on a different thread, the 
rescanCurrentDirectory should still work.

-

PR Review Comment: https://git.openjdk.org/jdk/pull/18109#discussion_r1543954407


Re: RFR: 8327137: Add test for ConcurrentModificationException in BasicDirectoryModel [v3]

2024-03-27 Thread Alexey Ivanov
On Fri, 22 Mar 2024 21:45:36 GMT, Alexey Ivanov  wrote:

>>>Instantiating JFileChooser on EDT adds complexity for no benefit. The 
>>>JFileChooser is still accessed concurrently from several threads, including 
>>>Swing internal thread.
>> 
>> But still, this is a common requirement to create UI components on EDT, and 
>> that will replicate more or less how it will work in the application.
>
>> > Instantiating JFileChooser on EDT adds complexity for no benefit. The 
>> > JFileChooser is still accessed concurrently from several threads, 
>> > including Swing internal thread.
>> 
>> But still, this is a common requirement to create UI components on EDT, and 
>> that will replicate more or less how it will work in the application.
> 
> And it is a common requirement ***not** to call 
> `fileChooser.rescanCurrentDirectory` concurrently* in 5 different threads. So 
> it does not apply.

In general, I agree all Swing components are to be created and accessed on EDT.

However, there are cases where instantiating and testing is safe on main 
thread. It is when the component is not shown on the screen, its state does not 
depend on any events dispatched by the event queue. There are such tests in the 
JDK code base.

I think this test is one of such cases where `JFileChooser` can be (safely) 
created on the current thread. The test does not display the file chooser, its 
state does not depend on receiving input events and handling them.

Essentially, the test verifies that *the implementation of 
`JFileChooser.rescanCurrentDirectory` is thread-safe*. It wasn't. Two bugs were 
resolved to make the implementation thread-safe: 
[JDK-8323670](https://bugs.openjdk.org/browse/JDK-8323670) and 
[JDK-8325179](https://bugs.openjdk.org/browse/JDK-8325179).

-

PR Review Comment: https://git.openjdk.org/jdk/pull/18109#discussion_r1541975141


Re: RFR: 8327137: Add test for ConcurrentModificationException in BasicDirectoryModel [v3]

2024-03-22 Thread Alexey Ivanov
On Fri, 22 Mar 2024 21:05:09 GMT, Sergey Bylokhov  wrote:

> > Instantiating JFileChooser on EDT adds complexity for no benefit. The 
> > JFileChooser is still accessed concurrently from several threads, including 
> > Swing internal thread.
> 
> But still, this is a common requirement to create UI components on EDT, and 
> that will replicate more or less how it will work in the application.

And it is a common requirement ***not** to call 
`fileChooser.rescanCurrentDirectory` concurrently* in 5 different threads. So 
it does not apply.

-

PR Review Comment: https://git.openjdk.org/jdk/pull/18109#discussion_r1536253490


Re: RFR: 8327137: Add test for ConcurrentModificationException in BasicDirectoryModel [v3]

2024-03-22 Thread Sergey Bylokhov
On Tue, 19 Mar 2024 17:03:25 GMT, Alexey Ivanov  wrote:

>Instantiating JFileChooser on EDT adds complexity for no benefit. The 
>JFileChooser is still accessed concurrently from several threads, including 
>Swing internal thread.

But still, this is a common requirement to create UI components on EDT, and 
that will replicate more or less how it will work in the application.

-

PR Review Comment: https://git.openjdk.org/jdk/pull/18109#discussion_r1536222501


Re: RFR: 8327137: Add test for ConcurrentModificationException in BasicDirectoryModel [v3]

2024-03-19 Thread Alexey Ivanov
On Fri, 15 Mar 2024 11:17:08 GMT, Alexey Ivanov  wrote:

>> test/jdk/javax/swing/plaf/basic/BasicDirectoryModel/ConcurrentModification.java
>>  line 211:
>> 
>>> 209: private static void deleteFile(final Path file) {
>>> 210: try {
>>> 211: Files.delete(file);
>> 
>> Should we try to delete all files first and then throw an exception if any?
>
> I do try to remove all the files:
> 
> https://github.com/openjdk/jdk/blob/0d6be7a4e4368ea67382af4321b9483236276e5a/test/jdk/javax/swing/plaf/basic/BasicDirectoryModel/ConcurrentModification.java#L115-L120
> 
> https://github.com/openjdk/jdk/blob/0d6be7a4e4368ea67382af4321b9483236276e5a/test/jdk/javax/swing/plaf/basic/BasicDirectoryModel/ConcurrentModification.java#L202-L215
> 
> Probably, this can be chained via `handleException`. On the other hand, I 
> don't want to fail the test if cleaning up the files fails. However, now an 
> exception thrown from the `finally` block will override any exception thrown 
> from the test code, which is a bad thing, so I'll need to reconsider 
> exception handling during clean-up.
> 
> If test fails to clean up the files it created, jtreg will remove them. It is 
> the reason why I use the current directory as the base, which is the 
> `scratch` directory when run in jtreg.

I updated the code to avoid throwing `IOException` from cleaning up code. If an 
exception occurs, its stack trace is printed to `stderr`. The test will not 
fail if cleaning up the files fails.

-

PR Review Comment: https://git.openjdk.org/jdk/pull/18109#discussion_r1530772076


Re: RFR: 8327137: Add test for ConcurrentModificationException in BasicDirectoryModel [v3]

2024-03-19 Thread Alexey Ivanov
On Tue, 19 Mar 2024 01:08:25 GMT, Sergey Bylokhov  wrote:

>> I don't think it matters here… The test does not rely on even processing on 
>> EDT, no state of `JFileChooser` is modified except for calling 
>> `rescanCurrentDirectory`. Technically, I am not allowed to do so either. But 
>> if I call `rescanCurrentDirectory` from EDT only, the test becomes void.
>
> I think it will fire the property change events via firePropertyChange from 
> the constructor and it is better to do that on EDT.

I guess it does fire property change events on the current thread rather than 
EDT in this case. I still don't think it matters in this case. After the 
`JFileChooser` is created, it is accessed on the Scanner threads only, and only 
to call its `rescanCurrentDirectory` method. This call, in its turn, creates a 
new background thread which accesses the fields and methods of `JFileChooser` 
as well as `FileSystemView`.

Instantiating `JFileChooser` on EDT adds complexity for no benefit. The 
`JFileChooser` is still accessed concurrently from several threads, including 
Swing internal thread.

-

PR Review Comment: https://git.openjdk.org/jdk/pull/18109#discussion_r1530766002


Re: RFR: 8327137: Add test for ConcurrentModificationException in BasicDirectoryModel [v3]

2024-03-18 Thread Sergey Bylokhov
On Fri, 15 Mar 2024 12:02:43 GMT, Alexey Ivanov  wrote:

>> test/jdk/javax/swing/plaf/basic/BasicDirectoryModel/ConcurrentModification.java
>>  line 99:
>> 
>>> 97: createFiles(temp);
>>> 98: 
>>> 99: final JFileChooser fc = new JFileChooser(temp.toFile());
>> 
>> it should be created on EDT, just in case.
>
> I don't think it matters here… The test does not rely on even processing on 
> EDT, no state of `JFileChooser` is modified except for calling 
> `rescanCurrentDirectory`. Technically, I am not allowed to do so either. But 
> if I call `rescanCurrentDirectory` from EDT only, the test becomes void.

I think it will fire the property change events via firePropertyChange from the 
constructor and it is better to do that on EDT.

-

PR Review Comment: https://git.openjdk.org/jdk/pull/18109#discussion_r1529545568


Re: RFR: 8327137: Add test for ConcurrentModificationException in BasicDirectoryModel [v3]

2024-03-15 Thread Alexey Ivanov
> I'm adding a regression test for 
> [JDK-8323670](https://bugs.openjdk.org/browse/JDK-8323670) and 
> [JDK-8307091](https://bugs.openjdk.org/browse/JDK-8307091); it's also a 
> regression test for 
> [JDK-8240690](https://bugs.openjdk.org/browse/JDK-8240690).
> 
> I referenced this test in PR #17462 in [this 
> comment](https://github.com/openjdk/jdk/pull/17462#issuecomment-1914844026). 
> I fine-tuned the test since that time.
> 
> The test doesn't fail all the time without the fix for JDK-8323670, however, 
> it fails sometimes. If you run the test several times, it will likely fail 
> _without the fix_.
> 
> For me, the test fails about 10 times from 40 runs in the CI. It fails on 
> macOS more frequently than on Linux.
> 
> When the test passes, it usually completes in 5 minutes.
> 
> **How the test works**
> 
> The test creates a temporary directory in the current directory and creates a 
> number of files in it. (The number of files is controlled by 
> `NUMBER_OF_THREADS` constant). Then the test creates `JFileChooser` in the 
> temporary directory.
> 
> The test starts several _scanner_ threads, the number is controlled by 
> `NUMBER_OF_THREADS`, which repeatedly call 
> `fileChooser.rescanCurrentDirectory()`. This results in calling 
> `BasicDirectoryModel.validateFileCache` which starts a background thread — 
> "Basic L&F File Loading Thread" — to enumerate the files.
> 
> A timer is used to create new files in the directory that the file chooser is 
> using.
> 
> After enumerating the files, the File Loading Thread posts an event to EDT. 
> The event updates `fileCache` and fires a `ListDataEvent`.
> 
> If the File Loading Thread is iterating over `fileCache` using `Iterator` 
> (when `fileCache.subList` or `fileCache.equals` is running; or a new `Vector` 
> instance is created from a `fileCache` or its sublist) and `fileCache` is 
> being updated on EDT, then `ConcurrentModificationException` is thrown.
> 
> On Linux and on _headless_ macOS, `ShellFolder.invoke` is executed in the 
> caller, which makes it easier to reproduce the issue. Because of 
> [JDK-8325179](https://bugs.openjdk.org/browse/JDK-8325179), there are several 
> File Loading Threads, which also helps to reproduce the issue.
> 
> On _headful_ macOS, the `BasicDirectoryModel` is not used, so the test does 
> not reproduce the issue.
> 
> On Windows, the test does not fail or fails with `OutOfMemoryError`. It is 
> because all the File Loading Threads are serialised on the COM thread, 
> `ShellFolder.invoke` submits the task to the COM thread and waits for it to 
> complete. The chance of updating `fileCache` whil...

Alexey Ivanov has updated the pull request incrementally with one additional 
commit since the last revision:

  Suppress throwing exceptions while deleting files

-

Changes:
  - all: https://git.openjdk.org/jdk/pull/18109/files
  - new: https://git.openjdk.org/jdk/pull/18109/files/0dfff701..73ea5bf4

Webrevs:
 - full: https://webrevs.openjdk.org/?repo=jdk&pr=18109&range=02
 - incr: https://webrevs.openjdk.org/?repo=jdk&pr=18109&range=01-02

  Stats: 15 lines in 1 file changed: 13 ins; 0 del; 2 mod
  Patch: https://git.openjdk.org/jdk/pull/18109.diff
  Fetch: git fetch https://git.openjdk.org/jdk.git pull/18109/head:pull/18109

PR: https://git.openjdk.org/jdk/pull/18109