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