You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by da...@apache.org on 2026/01/29 05:56:17 UTC
(jackrabbit-oak) branch OAK-12077 updated (0b41916e5d -> 1ef74a11a8)
This is an automated email from the ASF dual-hosted git repository.
daim pushed a change to branch OAK-12077
in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git
discard 0b41916e5d OAK-12077 : added joinUninterruptibly() in oak-commons
new 1ef74a11a8 OAK-12077 : added joinUninterruptibly() in oak-commons
This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version. This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:
* -- * -- B -- O -- O -- O (0b41916e5d)
\
N -- N -- N refs/heads/OAK-12077 (1ef74a11a8)
You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.
Any revisions marked "omit" are not gone; other references still
refer to them. Any revisions marked "discard" are gone forever.
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../internal/concurrent/UninterruptibleUtils.java | 23 +++++-----------
.../concurrent/UninterruptibleUtilsTest.java | 31 ++++++----------------
2 files changed, 14 insertions(+), 40 deletions(-)
(jackrabbit-oak) 01/01: OAK-12077 : added joinUninterruptibly() in oak-commons
Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
daim pushed a commit to branch OAK-12077
in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git
commit 1ef74a11a892d7bebd6f3ba18a25f0d7bd34462f
Author: rishabhdaim <ri...@gmail.com>
AuthorDate: Thu Jan 29 11:20:00 2026 +0530
OAK-12077 : added joinUninterruptibly() in oak-commons
---
.../internal/concurrent/UninterruptibleUtils.java | 35 +++++++
.../concurrent/UninterruptibleUtilsTest.java | 108 +++++++++++++++++++++
2 files changed, 143 insertions(+)
diff --git a/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/internal/concurrent/UninterruptibleUtils.java b/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/internal/concurrent/UninterruptibleUtils.java
index 6dcb24125b..5093ac0de3 100644
--- a/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/internal/concurrent/UninterruptibleUtils.java
+++ b/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/internal/concurrent/UninterruptibleUtils.java
@@ -110,4 +110,39 @@ public class UninterruptibleUtils {
}
}
}
+
+ /**
+ * Invokes {@link TimeUnit#timedJoin(Thread, long)} uninterruptibly.
+ * <p>
+ * This method repeatedly calls {@link TimeUnit#timedJoin(Thread, long)} until the
+ * specified timeout has elapsed or the target thread terminates, ignoring
+ * {@link InterruptedException} but remembering that an interruption
+ * occurred. When the method finally returns, it restores the current
+ * thread's interrupted status if any interruptions were detected.
+ *
+ * @param toJoin the thread to wait for; must not be {@code null}
+ * @throws NullPointerException if {@code toJoin} or {@code unit} is {@code null}
+ * @throws IllegalArgumentException if {@code timeout} is negative
+ */
+ public static void joinUninterruptibly(final Thread toJoin) {
+
+ Objects.requireNonNull(toJoin, "thread to join is null");
+
+ boolean interrupted = false;
+
+ try {
+ for(;;) {
+ try {
+ toJoin.join();
+ return;
+ } catch (InterruptedException var6) {
+ interrupted = true;
+ }
+ }
+ } finally {
+ if (interrupted) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
}
diff --git a/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/internal/concurrent/UninterruptibleUtilsTest.java b/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/internal/concurrent/UninterruptibleUtilsTest.java
index 1029a34a96..0e99f52084 100644
--- a/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/internal/concurrent/UninterruptibleUtilsTest.java
+++ b/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/internal/concurrent/UninterruptibleUtilsTest.java
@@ -162,4 +162,112 @@ public class UninterruptibleUtilsTest {
Assert.assertTrue("Zero sleep should return quickly", elapsedMillis < 50L);
}
+ @Test
+ public void testNullThread() {
+ Assert.assertThrows(NullPointerException.class,
+ () -> UninterruptibleUtils.joinUninterruptibly(null));
+ }
+
+ @Test
+ public void testReturnsWhenThreadFinishesBeforeTimeout() throws Exception {
+ final long workMillis = 10L;
+ final Thread worker = new Thread(() -> {
+ try {
+ Thread.sleep(workMillis);
+ } catch (InterruptedException ignored) {}
+ });
+
+ worker.start();
+
+ long start = System.nanoTime();
+ UninterruptibleUtils.joinUninterruptibly(worker);
+ long elapsedMillis = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
+
+ Assert.assertFalse("Worker should be finished", worker.isAlive());
+ Assert.assertTrue("Join should not take excessively long",
+ elapsedMillis >= workMillis && elapsedMillis < 100L);
+ }
+
+ @Test
+ public void testJoinShouldWaitUntilThreadFinishes() {
+ final Thread worker = new Thread(() -> {
+ // Run longer than the join timeout
+ try {
+ Thread.sleep(20L);
+ } catch (InterruptedException ignored) {
+ }
+ });
+
+ worker.start();
+
+ long start = System.nanoTime();
+ UninterruptibleUtils.joinUninterruptibly(worker);
+ long elapsedMillis = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
+
+ // Worker may still be alive (likely), but we at least checked the timeout behavior
+ Assert.assertTrue("Join should respect timeout", elapsedMillis >= 20L );
+ }
+
+ @Test
+ public void testJoinUninterruptiblyIgnoresInterruptsButRestoresFlag() throws Exception {
+ final Thread worker = new Thread(() -> {
+ try {
+ Thread.sleep(20L);
+ } catch (InterruptedException ignored) {
+ }
+ });
+
+ worker.start();
+
+ Thread joiningThread = new Thread(() -> {
+ UninterruptibleUtils.joinUninterruptibly(worker);
+ // After returning, interrupted flag should be set if we interrupted during join
+ Assert.assertTrue("Interrupt flag should be restored", Thread.currentThread().isInterrupted());
+ });
+
+ joiningThread.start();
+
+ // Let the joining thread enter join
+ Thread.sleep(50L);
+
+ // Interrupt while it is joining
+ joiningThread.interrupt();
+
+ joiningThread.join(200L);
+
+ Assert.assertFalse("Joining thread should have completed", joiningThread.isAlive());
+ Assert.assertFalse("Worker should have completed", worker.isAlive());
+ }
+
+ @Test
+ public void testJoinUninterruptiblyMultipleInterruptsStillCompleteAndRestoreFlag() throws Exception {
+ final Thread worker = new Thread(() -> {
+ try {
+ Thread.sleep(30L);
+ } catch (InterruptedException ignored) {
+ }
+ });
+
+ worker.start();
+
+ Thread joiningThread = new Thread(() -> {
+ UninterruptibleUtils.joinUninterruptibly(worker);
+ Assert.assertTrue("Interrupt flag should be restored after multiple interrupts",
+ Thread.currentThread().isInterrupted());
+ });
+
+ joiningThread.start();
+
+ // Interrupt the joining thread multiple times while it is waiting
+ for (int i = 0; i < 3; i++) {
+ Thread.sleep(50L);
+ joiningThread.interrupt();
+ }
+
+ joiningThread.join(2000L);
+
+ Assert.assertFalse("Joining thread should have completed", joiningThread.isAlive());
+ Assert.assertFalse("Worker should have completed", worker.isAlive());
+ }
+
}
\ No newline at end of file