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 * If currently asleep, stops sleeping; if not asleep, will skip the next 054 * sleep cycle. 055 */ 056 public void skipSleepCycle() { 057 synchronized (sleepLock) { 058 triggerWake = true; 059 sleepLock.notifyAll(); 060 } 061 } 062 063 /** 064 * Sleep for period. 065 */ 066 public void sleep() { 067 sleep(this.period); 068 } 069 070 public void sleep(long sleepTime) { 071 if (this.stopper.isStopped()) { 072 return; 073 } 074 long now = System.currentTimeMillis(); 075 long currentSleepTime = sleepTime; 076 while (currentSleepTime > 0) { 077 long woke = -1; 078 try { 079 synchronized (sleepLock) { 080 if (triggerWake) break; 081 sleepLock.wait(currentSleepTime); 082 } 083 woke = System.currentTimeMillis(); 084 long slept = woke - now; 085 if (slept - this.period > MINIMAL_DELTA_FOR_LOGGING) { 086 LOG.warn("We slept " + slept + "ms instead of " + this.period + 087 "ms, this is likely due to a long " + 088 "garbage collecting pause and it's usually bad, see " + 089 "http://hbase.apache.org/book.html#trouble.rs.runtime.zkexpired"); 090 } 091 } catch(InterruptedException iex) { 092 // We we interrupted because we're meant to stop? If not, just 093 // continue ignoring the interruption 094 if (this.stopper.isStopped()) { 095 return; 096 } 097 } 098 // Recalculate waitTime. 099 woke = (woke == -1)? System.currentTimeMillis(): woke; 100 currentSleepTime = this.period - (woke - now); 101 } 102 synchronized(sleepLock) { 103 triggerWake = false; 104 } 105 } 106 107 /** 108 * @return the sleep period in milliseconds 109 */ 110 public final int getPeriod() { 111 return period; 112 } 113}