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}