001/* 002 * jPOS Project [http://jpos.org] 003 * Copyright (C) 2000-2026 jPOS Software SRL 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU Affero General Public License as 007 * published by the Free Software Foundation, either version 3 of the 008 * License, or (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU Affero General Public License for more details. 014 * 015 * You should have received a copy of the GNU Affero General Public License 016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 017 */ 018 019package org.jpos.util; 020 021import java.util.concurrent.TimeUnit; 022import java.util.function.Supplier; 023 024/** 025 * Simple wall-clock timer that pads a block of work to a minimum duration, 026 * useful when masking timing differences between branches (e.g. successful 027 * vs. failed authentications). 028 */ 029public class StopWatch { 030 long end; 031 /** 032 * Constructs a StopWatch that finishes no earlier than {@code period} from now. 033 * 034 * @param period minimum duration to elapse before {@link #finish()} returns 035 * @param unit unit for {@code period} 036 */ 037 public StopWatch (long period, TimeUnit unit) { 038 end = System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(period, unit); 039 } 040 /** 041 * Convenience constructor that takes the minimum duration in milliseconds. 042 * 043 * @param periodInMillis minimum duration in milliseconds 044 */ 045 public StopWatch (long periodInMillis) { 046 this (periodInMillis, TimeUnit.MILLISECONDS); 047 } 048 /** Sleeps until the configured deadline, returning immediately when already past it. */ 049 public void finish() { 050 long now = System.currentTimeMillis(); 051 if (end > now) { 052 try { 053 Thread.sleep(end - now); 054 } catch (InterruptedException ignored) { } 055 } 056 } 057 /** 058 * Indicates whether the deadline has been reached. 059 * 060 * @return {@code true} if the configured period has elapsed 061 */ 062 public boolean isFinished() { 063 return System.currentTimeMillis() >= end; 064 } 065 066 /** 067 * Runs {@code f} and pads its execution to at least {@code period}. 068 * 069 * @param <T> result type returned by {@code f} 070 * @param period minimum duration to elapse 071 * @param unit unit for {@code period} 072 * @param f the work to perform 073 * @return the value returned by {@code f} 074 */ 075 public static <T> T get(long period, TimeUnit unit, Supplier<T> f) { 076 StopWatch w = new StopWatch(period, unit); 077 T t = f.get(); 078 w.finish(); 079 return t; 080 } 081 082 /** 083 * Runs {@code f} and pads its execution to at least {@code period} milliseconds. 084 * 085 * @param <T> result type returned by {@code f} 086 * @param period minimum duration in milliseconds 087 * @param f the work to perform 088 * @return the value returned by {@code f} 089 */ 090 public static <T> T get(long period, Supplier<T> f) { 091 return get(period, TimeUnit.MILLISECONDS, f); 092 } 093}