1 /** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 */ 19 package org.apache.hadoop.hbase.util; 20 21 import org.apache.commons.logging.Log; 22 import org.apache.commons.logging.LogFactory; 23 import org.apache.hadoop.hbase.classification.InterfaceAudience; 24 import org.apache.hadoop.hbase.Stoppable; 25 26 /** 27 * Sleeper for current thread. 28 * Sleeps for passed period. Also checks passed boolean and if interrupted, 29 * will return if the flag is set (rather than go back to sleep until its 30 * sleep time is up). 31 */ 32 @InterfaceAudience.Private 33 public class Sleeper { 34 private static final Log LOG = LogFactory.getLog(Sleeper.class); 35 private final int period; 36 private final Stoppable stopper; 37 private static final long MINIMAL_DELTA_FOR_LOGGING = 10000; 38 39 private final Object sleepLock = new Object(); 40 private boolean triggerWake = false; 41 42 /** 43 * @param sleep sleep time in milliseconds 44 * @param stopper When {@link Stoppable#isStopped()} is true, this thread will 45 * cleanup and exit cleanly. 46 */ Sleeper(final int sleep, final Stoppable stopper)47 public Sleeper(final int sleep, final Stoppable stopper) { 48 this.period = sleep; 49 this.stopper = stopper; 50 } 51 52 /** 53 * Sleep for period. 54 */ sleep()55 public void sleep() { 56 sleep(System.currentTimeMillis()); 57 } 58 59 /** 60 * If currently asleep, stops sleeping; if not asleep, will skip the next 61 * sleep cycle. 62 */ skipSleepCycle()63 public void skipSleepCycle() { 64 synchronized (sleepLock) { 65 triggerWake = true; 66 sleepLock.notifyAll(); 67 } 68 } 69 70 /** 71 * Sleep for period adjusted by passed <code>startTime</code> 72 * @param startTime Time some task started previous to now. Time to sleep 73 * will be docked current time minus passed <code>startTime</code>. 74 */ sleep(final long startTime)75 public void sleep(final long startTime) { 76 if (this.stopper.isStopped()) { 77 return; 78 } 79 long now = System.currentTimeMillis(); 80 long waitTime = this.period - (now - startTime); 81 if (waitTime > this.period) { 82 LOG.warn("Calculated wait time > " + this.period + 83 "; setting to this.period: " + System.currentTimeMillis() + ", " + 84 startTime); 85 waitTime = this.period; 86 } 87 while (waitTime > 0) { 88 long woke = -1; 89 try { 90 synchronized (sleepLock) { 91 if (triggerWake) break; 92 sleepLock.wait(waitTime); 93 } 94 woke = System.currentTimeMillis(); 95 long slept = woke - now; 96 if (slept - this.period > MINIMAL_DELTA_FOR_LOGGING) { 97 LOG.warn("We slept " + slept + "ms instead of " + this.period + 98 "ms, this is likely due to a long " + 99 "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 */ getPeriod()121 public final int getPeriod() { 122 return period; 123 } 124 } 125