View Javadoc

1   /**
2    * Copyright 2010 The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  package org.apache.hadoop.hbase.util;
21  
22  import org.apache.commons.logging.Log;
23  import org.apache.commons.logging.LogFactory;
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  public class Sleeper {
33    private final Log LOG = LogFactory.getLog(this.getClass().getName());
34    private final int period;
35    private final Stoppable stopper;
36    private static final long MINIMAL_DELTA_FOR_LOGGING = 10000;
37  
38    private final Object sleepLock = new Object();
39    private boolean triggerWake = false;
40  
41    /**
42     * @param sleep sleep time in milliseconds
43     * @param stopper When {@link Stoppable#isStopped()} is true, this thread will
44     * cleanup and exit cleanly.
45     */
46    public Sleeper(final int sleep, final Stoppable stopper) {
47      this.period = sleep;
48      this.stopper = stopper;
49    }
50  
51    /**
52     * Sleep for period.
53     */
54    public void sleep() {
55      sleep(System.currentTimeMillis());
56    }
57  
58    /**
59     * If currently asleep, stops sleeping; if not asleep, will skip the next
60     * sleep cycle.
61     */
62    public void skipSleepCycle() {
63      synchronized (sleepLock) {
64        triggerWake = true;
65        sleepLock.notifyAll();
66      }
67    }
68  
69    /**
70     * Sleep for period adjusted by passed <code>startTime<code>
71     * @param startTime Time some task started previous to now.  Time to sleep
72     * will be docked current time minus passed <code>startTime<code>.
73     */
74    public void sleep(final long startTime) {
75      if (this.stopper.isStopped()) {
76        return;
77      }
78      long now = System.currentTimeMillis();
79      long waitTime = this.period - (now - startTime);
80      if (waitTime > this.period) {
81        LOG.warn("Calculated wait time > " + this.period +
82          "; setting to this.period: " + System.currentTimeMillis() + ", " +
83          startTime);
84        waitTime = this.period;
85      }
86      while (waitTime > 0) {
87        long woke = -1;
88        try {
89          synchronized (sleepLock) {
90            if (triggerWake) break;
91            sleepLock.wait(waitTime);
92          }
93          woke = System.currentTimeMillis();
94          long slept = woke - now;
95          if (slept - this.period > MINIMAL_DELTA_FOR_LOGGING) {
96            LOG.warn("We slept " + slept + "ms instead of " + this.period +
97                "ms, this is likely due to a long " +
98                "garbage collecting pause and it's usually bad, see " +
99                "http://hbase.apache.org/book.html#trouble.rs.runtime.zkexpired");
100         }
101       } catch(InterruptedException iex) {
102         // We we interrupted because we're meant to stop?  If not, just
103         // continue ignoring the interruption
104         if (this.stopper.isStopped()) {
105           return;
106         }
107       }
108       // Recalculate waitTime.
109       woke = (woke == -1)? System.currentTimeMillis(): woke;
110       waitTime = this.period - (woke - startTime);
111     }
112     triggerWake = false;
113   }
114 }