| /** |
| * Copyright 2010 The Apache Software Foundation |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.hadoop.hbase.util; |
| |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.apache.hadoop.hbase.Stoppable; |
| |
| /** |
| * Sleeper for current thread. |
| * Sleeps for passed period. Also checks passed boolean and if interrupted, |
| * will return if the flag is set (rather than go back to sleep until its |
| * sleep time is up). |
| */ |
| public class Sleeper { |
| private final Log LOG = LogFactory.getLog(this.getClass().getName()); |
| private final int period; |
| private final Stoppable stopper; |
| private static final long MINIMAL_DELTA_FOR_LOGGING = 10000; |
| |
| private final Object sleepLock = new Object(); |
| private boolean triggerWake = false; |
| |
| /** |
| * @param sleep sleep time in milliseconds |
| * @param stopper When {@link Stoppable#isStopped()} is true, this thread will |
| * cleanup and exit cleanly. |
| */ |
| public Sleeper(final int sleep, final Stoppable stopper) { |
| this.period = sleep; |
| this.stopper = stopper; |
| } |
| |
| /** |
| * Sleep for period. |
| */ |
| public void sleep() { |
| sleep(System.currentTimeMillis()); |
| } |
| |
| /** |
| * If currently asleep, stops sleeping; if not asleep, will skip the next |
| * sleep cycle. |
| */ |
| public void skipSleepCycle() { |
| synchronized (sleepLock) { |
| triggerWake = true; |
| sleepLock.notify(); |
| } |
| } |
| |
| /** |
| * Sleep for period adjusted by passed <code>startTime<code> |
| * @param startTime Time some task started previous to now. Time to sleep |
| * will be docked current time minus passed <code>startTime<code>. |
| */ |
| public void sleep(final long startTime) { |
| if (this.stopper.isStopped()) { |
| return; |
| } |
| long now = System.currentTimeMillis(); |
| long waitTime = this.period - (now - startTime); |
| if (waitTime > this.period) { |
| LOG.warn("Calculated wait time > " + this.period + |
| "; setting to this.period: " + System.currentTimeMillis() + ", " + |
| startTime); |
| waitTime = this.period; |
| } |
| while (waitTime > 0) { |
| long woke = -1; |
| try { |
| synchronized (sleepLock) { |
| if (triggerWake) break; |
| sleepLock.wait(waitTime); |
| } |
| woke = System.currentTimeMillis(); |
| long slept = woke - now; |
| if (slept - this.period > MINIMAL_DELTA_FOR_LOGGING) { |
| LOG.warn("We slept " + slept + "ms instead of " + this.period + |
| "ms, this is likely due to a long " + |
| "garbage collecting pause and it's usually bad, " + |
| "see http://wiki.apache.org/hadoop/Hbase/Troubleshooting#A9"); |
| } |
| } catch(InterruptedException iex) { |
| // We we interrupted because we're meant to stop? If not, just |
| // continue ignoring the interruption |
| if (this.stopper.isStopped()) { |
| return; |
| } |
| } |
| // Recalculate waitTime. |
| woke = (woke == -1)? System.currentTimeMillis(): woke; |
| waitTime = this.period - (woke - startTime); |
| } |
| triggerWake = false; |
| } |
| } |