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) { 081 break; 082 } 083 084 sleepLock.wait(currentSleepTime); 085 } 086 woke = System.currentTimeMillis(); 087 long slept = woke - now; 088 if (slept - this.period > MINIMAL_DELTA_FOR_LOGGING) { 089 LOG.warn("We slept {}ms instead of {}ms, this is likely due to a long " + 090 "garbage collecting pause and it's usually bad, see " + 091 "http://hbase.apache.org/book.html#trouble.rs.runtime.zkexpired", slept, this.period); 092 } 093 } catch(InterruptedException iex) { 094 // We we interrupted because we're meant to stop? If not, just 095 // continue ignoring the interruption 096 if (this.stopper.isStopped()) { 097 return; 098 } 099 } 100 // Recalculate waitTime. 101 woke = (woke == -1)? System.currentTimeMillis(): woke; 102 currentSleepTime = this.period - (woke - now); 103 } 104 synchronized(sleepLock) { 105 triggerWake = false; 106 } 107 } 108 109 /** 110 * @return the sleep period in milliseconds 111 */ 112 public final int getPeriod() { 113 return period; 114 } 115}