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.classification.InterfaceStability;
25  import org.apache.hadoop.hbase.Stoppable;
26  
27  /**
28   * Sleeper for current thread.
29   * Sleeps for passed period.  Also checks passed boolean and if interrupted,
30   * will return if the flag is set (rather than go back to sleep until its
31   * sleep time is up).
32   */
33  @InterfaceAudience.Public
34  @InterfaceStability.Stable
35  public class Sleeper {
36    private final Log LOG = LogFactory.getLog(this.getClass().getName());
37    private final int period;
38    private final Stoppable stopper;
39    private static final long MINIMAL_DELTA_FOR_LOGGING = 10000;
40  
41    private final Object sleepLock = new Object();
42    private boolean triggerWake = false;
43  
44    /**
45     * @param sleep sleep time in milliseconds
46     * @param stopper When {@link Stoppable#isStopped()} is true, this thread will
47     * cleanup and exit cleanly.
48     */
49    public Sleeper(final int sleep, final Stoppable stopper) {
50      this.period = sleep;
51      this.stopper = stopper;
52    }
53  
54    /**
55     * Sleep for period.
56     */
57    public void sleep() {
58      sleep(System.currentTimeMillis());
59    }
60  
61    /**
62     * If currently asleep, stops sleeping; if not asleep, will skip the next
63     * sleep cycle.
64     */
65    public void skipSleepCycle() {
66      synchronized (sleepLock) {
67        triggerWake = true;
68        sleepLock.notifyAll();
69      }
70    }
71  
72    /**
73     * Sleep for period adjusted by passed <code>startTime<code>
74     * @param startTime Time some task started previous to now.  Time to sleep
75     * will be docked current time minus passed <code>startTime<code>.
76     */
77    public void sleep(final long startTime) {
78      if (this.stopper.isStopped()) {
79        return;
80      }
81      long now = System.currentTimeMillis();
82      long waitTime = this.period - (now - startTime);
83      if (waitTime > this.period) {
84        LOG.warn("Calculated wait time > " + this.period +
85          "; setting to this.period: " + System.currentTimeMillis() + ", " +
86          startTime);
87        waitTime = this.period;
88      }
89      while (waitTime > 0) {
90        long woke = -1;
91        try {
92          synchronized (sleepLock) {
93            if (triggerWake) break;
94            sleepLock.wait(waitTime);
95          }
96          woke = System.currentTimeMillis();
97          long slept = woke - now;
98          if (slept - this.period > MINIMAL_DELTA_FOR_LOGGING) {
99            LOG.warn("We slept " + slept + "ms instead of " + this.period +
100               "ms, this is likely due to a long " +
101               "garbage collecting pause and it's usually bad, see " +
102               "http://hbase.apache.org/book.html#trouble.rs.runtime.zkexpired");
103         }
104       } catch(InterruptedException iex) {
105         // We we interrupted because we're meant to stop?  If not, just
106         // continue ignoring the interruption
107         if (this.stopper.isStopped()) {
108           return;
109         }
110       }
111       // Recalculate waitTime.
112       woke = (woke == -1)? System.currentTimeMillis(): woke;
113       waitTime = this.period - (woke - startTime);
114     }
115     triggerWake = false;
116   }
117 }