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