This is an automated email from the ASF dual-hosted git repository.

daim pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 1a66f4d087 OAK-12076 : added sleepUninterruptibly() in oak-commons 
(#2705)
1a66f4d087 is described below

commit 1a66f4d087fded053dbd93bce3a86fbd8118b4a1
Author: Rishabh Kumar <[email protected]>
AuthorDate: Thu Jan 29 10:35:04 2026 +0530

    OAK-12076 : added sleepUninterruptibly() in oak-commons (#2705)
---
 .../internal/concurrent/UninterruptibleUtils.java  | 46 +++++++++++
 .../concurrent/UninterruptibleUtilsTest.java       | 88 ++++++++++++++++++++++
 2 files changed, 134 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 dfd908469e..6dcb24125b 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
@@ -20,6 +20,7 @@ package org.apache.jackrabbit.oak.commons.internal.concurrent;
 
 import java.util.Objects;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Utility methods for waiting on synchronization primitives without
@@ -64,4 +65,49 @@ public class UninterruptibleUtils {
             }
         }
     }
+
+    /**
+     * Causes the current thread to sleep for the specified duration,
+     * ignoring {@link InterruptedException} until the full sleep time
+     * has elapsed, then restores the interrupted status before returning.
+     * <p>
+     * This behaves like Guava's
+     * {@code Uninterruptibles.sleepUninterruptibly(long, TimeUnit)}:
+     * it repeatedly invokes {@link TimeUnit#sleep(long)} until the
+     * requested time has passed, catching and recording interruptions
+     * and recomputing the remaining time from a fixed deadline.
+     *
+     * @param sleep the time to sleep; must be non-negative
+     * @param unit     the time unit of the {@code sleep} argument; must not 
be {@code null}
+     * @throws NullPointerException     if {@code unit} is {@code null}
+     * @throws IllegalArgumentException if {@code sleep} is negative
+     */
+    public static void sleepUninterruptibly(final long sleep, final TimeUnit 
unit) {
+
+        Objects.requireNonNull(unit, "timeunit is null");
+
+        if (sleep < 0L) {
+            throw new IllegalArgumentException("sleep must be >= 0");
+        }
+
+        boolean interrupted = false;
+        try {
+            long remainingNanos = unit.toNanos(sleep);
+            long end = System.nanoTime() + remainingNanos;
+            for (;;) {
+                try {
+                    // TimeUnit.sleep() treats negative timeouts just like 
zero.
+                    TimeUnit.NANOSECONDS.sleep(remainingNanos);
+                    return;
+                } catch (InterruptedException e) {
+                    interrupted = true;
+                    remainingNanos = end - System.nanoTime();
+                }
+            }
+        } 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 45f945c9a1..1029a34a96 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
@@ -22,6 +22,7 @@ import org.junit.Assert;
 import org.junit.Test;
 
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Unit cases for {@link UninterruptibleUtils}
@@ -74,4 +75,91 @@ public class UninterruptibleUtilsTest {
         Assert.assertFalse(t.isAlive());
     }
 
+    @Test
+    public void testNullTimeUnit() {
+        Assert.assertThrows(NullPointerException.class, () -> 
UninterruptibleUtils.sleepUninterruptibly(1L, null));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void sleepUninterruptibly_negativeSleepThrowsIae() {
+        UninterruptibleUtils.sleepUninterruptibly(-1L, TimeUnit.MILLISECONDS);
+    }
+
+    @Test
+    public void testSleepsForAtLeastRequestedTime() {
+        long sleepMillis = 20L;
+        long start = System.nanoTime();
+
+        UninterruptibleUtils.sleepUninterruptibly(sleepMillis, 
TimeUnit.MILLISECONDS);
+
+        long elapsedMillis = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - 
start);
+
+        Assert.assertTrue("Elapsed should be at least requested sleep",
+                elapsedMillis >= sleepMillis - 10); // small margin
+    }
+
+    @Test
+    public void testIgnoresInterruptsButRestoresFlag() throws Exception {
+        final long sleepMillis = 20L;
+
+        Thread t = new Thread(new Runnable() {
+            @Override
+            public void run() {
+                UninterruptibleUtils.sleepUninterruptibly(sleepMillis, 
TimeUnit.MILLISECONDS);
+                // After returning, interrupted flag should be set
+                Assert.assertTrue("Interrupt flag should be restored",
+                        Thread.currentThread().isInterrupted());
+            }
+        });
+
+        t.start();
+
+        // Let the thread enter sleep
+        Thread.sleep(5);
+
+        // Interrupt during sleep
+        t.interrupt();
+
+        t.join(20);
+
+        Assert.assertFalse("Thread should have completed sleep", t.isAlive());
+    }
+
+    @Test
+    public void testMultipleInterruptsStillCompleteAndRestoreFlag() throws 
Exception {
+        final long sleepMillis = 20L;
+
+        Thread t = new Thread(new Runnable() {
+            @Override
+            public void run() {
+                UninterruptibleUtils.sleepUninterruptibly(sleepMillis, 
TimeUnit.MILLISECONDS);
+                Assert.assertTrue("Interrupt flag should be restored after 
multiple interrupts",
+                        Thread.currentThread().isInterrupted());
+            }
+        });
+
+        t.start();
+
+        // Interrupt the thread multiple times while it is sleeping
+        for (int i = 0; i < 3; i++) {
+            Thread.sleep(5);
+            t.interrupt();
+        }
+
+        t.join(20);
+
+        Assert.assertFalse("Thread should have completed sleep", t.isAlive());
+    }
+
+    @Test
+    public void testZeroSleepReturnsQuickly() {
+        long start = System.nanoTime();
+
+        UninterruptibleUtils.sleepUninterruptibly(0L, TimeUnit.MILLISECONDS);
+
+        long elapsedMillis = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - 
start);
+
+        Assert.assertTrue("Zero sleep should return quickly", elapsedMillis < 
50L);
+    }
+
 }
\ No newline at end of file

Reply via email to