001/*
002 * Copyright 2023 the original author or authors.
003 * <p>
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 * <p>
008 * https://www.apache.org/licenses/LICENSE-2.0
009 * <p>
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package de.cuioss.tools.concurrent;
017
018import static java.util.concurrent.TimeUnit.NANOSECONDS;
019
020import java.time.Duration;
021import java.util.concurrent.TimeUnit;
022
023import lombok.experimental.UtilityClass;
024
025/**
026 * Provides some helper-methods taken from com.google.common.util.concurrent
027 * package
028 *
029 * @author Oliver Wolff
030 *
031 */
032@UtilityClass
033public class ConcurrentTools {
034
035    /**
036     * Invokes {@code unit.}{@link TimeUnit#sleep(long) sleep(sleepFor)}
037     * uninterruptibly.
038     *
039     * @param sleepFor
040     *
041     */
042    public static void sleepUninterruptibly(Duration sleepFor) {
043        sleepUninterruptibly(saturatedToNanos(sleepFor), TimeUnit.NANOSECONDS);
044    }
045
046    /**
047     * Invokes {@code unit.}{@link TimeUnit#sleep(long) sleep(sleepFor)}
048     * uninterruptibly.
049     *
050     * @param sleepFor
051     * @param unit
052     */
053    public static void sleepUninterruptibly(long sleepFor, TimeUnit unit) {
054        var interrupted = false;
055        try {
056            var remainingNanos = unit.toNanos(sleepFor);
057            var end = System.nanoTime() + remainingNanos;
058            while (true) {
059                try {
060                    // TimeUnit.sleep() treats negative timeouts just like zero.
061                    NANOSECONDS.sleep(remainingNanos);
062                    return;
063                } catch (InterruptedException e) {
064                    interrupted = true;
065                    remainingNanos = end - System.nanoTime();
066                }
067            }
068        } finally {
069            if (interrupted) {
070                Thread.currentThread().interrupt();
071            }
072        }
073    }
074
075    /**
076     * Returns the number of nanoseconds of the given duration without throwing or
077     * overflowing.
078     *
079     * <p>
080     * Instead of throwing {@link ArithmeticException}, this method silently
081     * saturates to either {@link Long#MAX_VALUE} or {@link Long#MIN_VALUE}. This
082     * behavior can be useful when decomposing a duration in order to call a legacy
083     * API which requires a {@code long, TimeUnit} pair.
084     *
085     * @author com.google.common.util.concurrent.Internal
086     */
087    static long saturatedToNanos(Duration duration) {
088        // Using a try/catch seems lazy, but the catch block will rarely get invoked
089        // (except for
090        // durations longer than approximately +/- 292 years).
091        try {
092            return duration.toNanos();
093        } catch (ArithmeticException tooBig) {
094            return duration.isNegative() ? Long.MIN_VALUE : Long.MAX_VALUE;
095        }
096    }
097}