Skip to content

Commit 7aed9ce

Browse files
committed
Fix thread-safety in BlockingIO
1 parent 99e614f commit 7aed9ce

File tree

2 files changed

+19
-7
lines changed

2 files changed

+19
-7
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,8 @@ HttpURLConnection conn = (HttpURLConnection)(new URL(url).openConnection());
432432
BlockingIO.register(conn::disconnect);
433433
```
434434

435+
`BlockingIO` is a late addition to the Tascalate Concurrent, it's available only since version [0.9.5](https://github.com/vsilaev/tascalate-concurrent/releases/tag/0.9.5).
436+
435437
Unfortunately, it's necessary to put this statement straight at the end: if the blocking API is hidden behind third-party library code (like Spring RestTemplate) then you are out of luck - Tascalate Concurrent will not support true interruptions in this scenario.
436438

437439
## 5. Overriding default asynchronous executor

src/main/java/net/tascalate/concurrent/io/BlockingIO.java

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
*/
1616
package net.tascalate.concurrent.io;
1717

18-
import java.util.LinkedList;
1918
import java.util.List;
19+
import java.util.concurrent.CopyOnWriteArrayList;
2020
import java.util.function.BiConsumer;
2121
import java.util.function.BiFunction;
2222
import java.util.function.BiPredicate;
@@ -30,7 +30,7 @@ final public class BlockingIO {
3030
private final static ThreadLocal<Interruptible> CURRENT = new ThreadLocal<>();
3131

3232
final static class Interruptible {
33-
private final List<AutoCloseable> closeables = new LinkedList<>();
33+
private final List<AutoCloseable> closeables = new CopyOnWriteArrayList<>();
3434
private final BlockingThreadSelector selector = new BlockingThreadSelector(this::interrupted);
3535

3636
final Interruptible enter() {
@@ -52,17 +52,27 @@ final void exit(Interruptible previous) {
5252

5353
void interrupted() {
5454
for (AutoCloseable closeable : closeables) {
55-
try {
56-
closeable.close();
57-
} catch (Exception ex) {
58-
59-
}
55+
close(closeable);
6056
}
6157
closeables.clear();
6258
}
6359

6460
void enlist(AutoCloseable closeOnInterruption) {
6561
closeables.add(closeOnInterruption);
62+
// For possible race - check for thread interruption after addition to the list
63+
// This way closeOnInterruption.close() might be called twice in worst case,
64+
// but it will be always called at least once (in case of interruption)
65+
if (Thread.currentThread().isInterrupted()) {
66+
close(closeOnInterruption);
67+
}
68+
}
69+
70+
private static void close(AutoCloseable closeable) {
71+
try {
72+
closeable.close();
73+
} catch (Exception ex) {
74+
75+
}
6676
}
6777
}
6878

0 commit comments

Comments
 (0)