001/** 002 * 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 */ 019package org.apache.hadoop.hbase.util; 020 021import org.apache.hadoop.hbase.Stoppable; 022import org.apache.yetus.audience.InterfaceAudience; 023import org.slf4j.Logger; 024import org.slf4j.LoggerFactory; 025 026/** 027 * Sleeper for current thread. 028 * Sleeps for passed period. Also checks passed boolean and if interrupted, 029 * will return if the flag is set (rather than go back to sleep until its 030 * sleep time is up). 031 */ 032@InterfaceAudience.Private 033public class Sleeper { 034 private static final Logger LOG = LoggerFactory.getLogger(Sleeper.class); 035 private final int period; 036 private final Stoppable stopper; 037 private static final long MINIMAL_DELTA_FOR_LOGGING = 10000; 038 039 private final Object sleepLock = new Object(); 040 private boolean triggerWake = false; 041 042 /** 043 * @param sleep sleep time in milliseconds 044 * @param stopper When {@link Stoppable#isStopped()} is true, this thread will 045 * cleanup and exit cleanly. 046 */ 047 public Sleeper(final int sleep, final Stoppable stopper) { 048 this.period = sleep; 049 this.stopper = stopper; 050 } 051 052 /** 053 * Sleep for period. 054 */ 055 public void sleep() { 056 sleep(System.currentTimeMillis()); 057 } 058 059 /** 060 * If currently asleep, stops sleeping; if not asleep, will skip the next 061 * sleep cycle. 062 */ 063 public void skipSleepCycle() { 064 synchronized (sleepLock) { 065 triggerWake = true; 066 sleepLock.notifyAll(); 067 } 068 } 069 070 /** 071 * Sleep for period adjusted by passed <code>startTime</code> 072 * @param startTime Time some task started previous to now. Time to sleep 073 * will be docked current time minus passed <code>startTime</code>. 074 */ 075 public void sleep(final long startTime) { 076 if (this.stopper.isStopped()) { 077 return; 078 } 079 long now = System.currentTimeMillis(); 080 long waitTime = this.period - (now - startTime); 081 if (waitTime > this.period) { 082 LOG.warn("Calculated wait time > " + this.period + 083 "; setting to this.period: " + System.currentTimeMillis() + ", " + 084 startTime); 085 waitTime = this.period; 086 } 087 while (waitTime > 0) { 088 long woke = -1; 089 try { 090 synchronized (sleepLock) { 091 if (triggerWake) break; 092 sleepLock.wait(waitTime); 093 } 094 woke = System.currentTimeMillis(); 095 long slept = woke - now; 096 if (slept - this.period > MINIMAL_DELTA_FOR_LOGGING) { 097 LOG.warn("We slept " + slept + "ms instead of " + this.period + 098 "ms, this is likely due to a long " + 099 "garbage collecting pause and it's usually bad, see " + 100 "http://hbase.apache.org/book.html#trouble.rs.runtime.zkexpired"); 101 } 102 } catch(InterruptedException iex) { 103 // We we interrupted because we're meant to stop? If not, just 104 // continue ignoring the interruption 105 if (this.stopper.isStopped()) { 106 return; 107 } 108 } 109 // Recalculate waitTime. 110 woke = (woke == -1)? System.currentTimeMillis(): woke; 111 waitTime = this.period - (woke - startTime); 112 } 113 synchronized(sleepLock) { 114 triggerWake = false; 115 } 116 } 117 118 /** 119 * @return the sleep period in milliseconds 120 */ 121 public final int getPeriod() { 122 return period; 123 } 124}