View Javadoc

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.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 final Log LOG = LogFactory.getLog(this.getClass().getName());
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     */
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     */
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     */
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     */
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     triggerWake = false;
114   }
115 }