Skip to content

Commit

Permalink
Don't clear interrupt with Thread.interrupted
Browse files Browse the repository at this point in the history
Allows caller to spawn a thread, execute, interrupt, and join.

Fixes #1991
  • Loading branch information
jhy committed Sep 8, 2023
1 parent 1e69577 commit 04b3a82
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 3 deletions.
5 changes: 5 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ Release 1.16.2 [PENDING]
ASCII.
<https://github.com/jhy/jsoup/issues/1952>

* Bugfix: in Jsoup.connect(url), the ConstrainableInputStream would clear Thread interrupts when reading the body.
This precluded callers from spawning a thread, running a number of requests for a length of time, then joining that
thread after interrupting it.
<https://github.com/jhy/jsoup/issues/1991>

* Change: removed previously deprecated methods Document#normalise, Element#forEach(org.jsoup.helper.Consumer<>),
Node#forEach(org.jsoup.helper.Consumer<>), and the org.jsoup.helper.Consumer interface; the latter being a
previously required compatibility shim prior to Android's de-sugaring support.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ public static ConstrainableInputStream wrap(InputStream in, int bufferSize, int
public int read(byte[] b, int off, int len) throws IOException {
if (interrupted || capped && remaining <= 0)
return -1;
if (Thread.interrupted()) {
// interrupted latches, because parse() may call twice (and we still want the thread interupt to clear)
if (Thread.currentThread().isInterrupted()) {
// interrupted latches, because parse() may call twice
interrupted = true;
return -1;
}
Expand Down
25 changes: 24 additions & 1 deletion src/test/java/org/jsoup/integration/ConnectIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import java.net.SocketTimeoutException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicBoolean;

import static org.junit.jupiter.api.Assertions.*;

Expand Down Expand Up @@ -70,7 +71,29 @@ public void canInterruptDocumentRead() throws InterruptedException {
assertTrue(runner.isInterrupted());
runner.join();

assertEquals(0, body[0].length()); // doesn't ready a failed doc
assertEquals(0, body[0].length()); // doesn't read a failed doc
}

@Test public void canInterruptThenJoinASpawnedThread() throws InterruptedException {
// https://github.com/jhy/jsoup/issues/1991
AtomicBoolean ioException = new AtomicBoolean();
Thread runner = new Thread(() -> {
try {
while (!Thread.currentThread().isInterrupted()) {
Document doc = Jsoup.connect(SlowRider.Url)
.timeout(4000)
.get();
}
} catch (IOException e) {
ioException.set(true); // don't expect to catch, because the outer sleep will complete before this timeout
}
});

runner.start();
Thread.sleep(2 * 1000);
runner.interrupt();
runner.join();
assertFalse(ioException.get());
}

@Test
Expand Down

0 comments on commit 04b3a82

Please sign in to comment.