You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by mb...@apache.org on 2014/11/12 00:24:23 UTC

[2/3] hbase git commit: HBASE-12460 Moving Chore to hbase-common module

HBASE-12460 Moving Chore to hbase-common module

Signed-off-by: Matteo Bertozzi <ma...@cloudera.com>


Project: http://git-wip-us.apache.org/repos/asf/hbase/repo
Commit: http://git-wip-us.apache.org/repos/asf/hbase/commit/133c79a3
Tree: http://git-wip-us.apache.org/repos/asf/hbase/tree/133c79a3
Diff: http://git-wip-us.apache.org/repos/asf/hbase/diff/133c79a3

Branch: refs/heads/branch-1
Commit: 133c79a391793cdfced86f3818cd4bf1873a9bd2
Parents: bebf018
Author: Srikanth Srungarapu <ss...@cloudera.com>
Authored: Tue Nov 11 14:47:48 2014 -0800
Committer: Matteo Bertozzi <ma...@cloudera.com>
Committed: Tue Nov 11 23:18:37 2014 +0000

----------------------------------------------------------------------
 .../java/org/apache/hadoop/hbase/Chore.java     | 142 -------------------
 .../java/org/apache/hadoop/hbase/Stoppable.java |  38 -----
 .../org/apache/hadoop/hbase/util/HasThread.java | 100 -------------
 .../org/apache/hadoop/hbase/util/Sleeper.java   | 117 ---------------
 .../java/org/apache/hadoop/hbase/Chore.java     | 142 +++++++++++++++++++
 .../java/org/apache/hadoop/hbase/Stoppable.java |  38 +++++
 .../org/apache/hadoop/hbase/util/HasThread.java | 100 +++++++++++++
 .../org/apache/hadoop/hbase/util/Sleeper.java   | 117 +++++++++++++++
 8 files changed, 397 insertions(+), 397 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/hbase/blob/133c79a3/hbase-client/src/main/java/org/apache/hadoop/hbase/Chore.java
----------------------------------------------------------------------
diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/Chore.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/Chore.java
deleted file mode 100644
index c2c7964..0000000
--- a/hbase-client/src/main/java/org/apache/hadoop/hbase/Chore.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/**
- *
- * 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;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.hadoop.hbase.classification.InterfaceAudience;
-import org.apache.hadoop.hbase.util.HasThread;
-import org.apache.hadoop.hbase.util.Sleeper;
-
-/**
- * Chore is a task performed on a period in hbase.  The chore is run in its own
- * thread. This base abstract class provides while loop and sleeping facility.
- * If an unhandled exception, the threads exit is logged.
- * Implementers just need to add checking if there is work to be done and if
- * so, do it.  Its the base of most of the chore threads in hbase.
- *
- * <p>Don't subclass Chore if the task relies on being woken up for something to
- * do, such as an entry being added to a queue, etc.
- */
-@InterfaceAudience.Private
-public abstract class Chore extends HasThread {
-  private final Log LOG = LogFactory.getLog(this.getClass());
-  private final Sleeper sleeper;
-  protected final Stoppable stopper;
-
-  /**
-   * @param p Period at which we should run.  Will be adjusted appropriately
-   * should we find work and it takes time to complete.
-   * @param stopper When {@link Stoppable#isStopped()} is true, this thread will
-   * cleanup and exit cleanly.
-   */
-  public Chore(String name, final int p, final Stoppable stopper) {
-    super(name);
-    if (stopper == null){
-      throw new NullPointerException("stopper cannot be null");
-    }
-    this.sleeper = new Sleeper(p, stopper);
-    this.stopper = stopper;
-  }
-
-  /**
-   * This constructor is for test only. It allows to create an object and to call chore() on
-   *  it. There is no sleeper nor stoppable.
-   */
-  protected Chore(){
-    sleeper = null;
-    stopper = null;
-  }
-
-  /**
-   * @see java.lang.Thread#run()
-   */
-  @Override
-  public void run() {
-    try {
-      boolean initialChoreComplete = false;
-      while (!this.stopper.isStopped()) {
-        long startTime = System.currentTimeMillis();
-        try {
-          if (!initialChoreComplete) {
-            initialChoreComplete = initialChore();
-          } else {
-            chore();
-          }
-        } catch (Exception e) {
-          LOG.error("Caught exception", e);
-          if (this.stopper.isStopped()) {
-            continue;
-          }
-        }
-        this.sleeper.sleep(startTime);
-      }
-    } catch (Throwable t) {
-      LOG.fatal(getName() + "error", t);
-    } finally {
-      LOG.info(getName() + " exiting");
-      cleanup();
-    }
-  }
-
-  /**
-   * If the thread is currently sleeping, trigger the core to happen immediately.
-   * If it's in the middle of its operation, will begin another operation
-   * immediately after finishing this one.
-   */
-  public void triggerNow() {
-    this.sleeper.skipSleepCycle();
-  }
-
-  /*
-   * Exposed for TESTING!
-   * calls directly the chore method, from the current thread.
-   */
-  public void choreForTesting() {
-    chore();
-  }
-
-  /**
-   * Override to run a task before we start looping.
-   * @return true if initial chore was successful
-   */
-  protected boolean initialChore() {
-    // Default does nothing.
-    return true;
-  }
-
-  /**
-   * Look for chores.  If any found, do them else just return.
-   */
-  protected abstract void chore();
-
-  /**
-   * Sleep for period.
-   */
-  protected void sleep() {
-    this.sleeper.sleep();
-  }
-
-  /**
-   * Called when the chore has completed, allowing subclasses to cleanup any
-   * extra overhead
-   */
-  protected void cleanup() {
-  }
-}

http://git-wip-us.apache.org/repos/asf/hbase/blob/133c79a3/hbase-client/src/main/java/org/apache/hadoop/hbase/Stoppable.java
----------------------------------------------------------------------
diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/Stoppable.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/Stoppable.java
deleted file mode 100644
index b87b764..0000000
--- a/hbase-client/src/main/java/org/apache/hadoop/hbase/Stoppable.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/**
- *
- * 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;
-
-import org.apache.hadoop.hbase.classification.InterfaceAudience;
-
-/**
- * Implementers are Stoppable.
- */
-@InterfaceAudience.Private
-public interface Stoppable {
-  /**
-   * Stop this service.
-   * @param why Why we're stopping.
-   */
-  void stop(String why);
-
-  /**
-   * @return True if {@link #stop(String)} has been closed.
-   */
-  boolean isStopped();
-}

http://git-wip-us.apache.org/repos/asf/hbase/blob/133c79a3/hbase-client/src/main/java/org/apache/hadoop/hbase/util/HasThread.java
----------------------------------------------------------------------
diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/util/HasThread.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/util/HasThread.java
deleted file mode 100644
index 4457fe0..0000000
--- a/hbase-client/src/main/java/org/apache/hadoop/hbase/util/HasThread.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/**
- * 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.hadoop.hbase.classification.InterfaceAudience;
-
-import java.lang.Thread.UncaughtExceptionHandler;
-
-/**
- * Abstract class which contains a Thread and delegates the common Thread
- * methods to that instance.
- * 
- * The purpose of this class is to workaround Sun JVM bug #6915621, in which
- * something internal to the JDK uses Thread.currentThread() as a monitor
- * lock. This can produce deadlocks like HBASE-4367, HBASE-4101, etc.
- */
-@InterfaceAudience.Private
-public abstract class HasThread implements Runnable {
-  private final Thread thread;
-  
-  public HasThread() {
-    this.thread = new Thread(this);
-  }
-
-  public HasThread(String name) {
-    this.thread = new Thread(this, name);
-  }
-  
-  public Thread getThread() {
-    return thread;
-  }
-  
-  public abstract void run();
-  
-  //// Begin delegation to Thread
-  
-  public final String getName() {
-    return thread.getName();
-  }
-
-  public void interrupt() {
-    thread.interrupt();
-  }
-
-  public final boolean isAlive() {
-    return thread.isAlive();
-  }
-
-  public boolean isInterrupted() {
-    return thread.isInterrupted();
-  }
-
-  public final void setDaemon(boolean on) {
-    thread.setDaemon(on);
-  }
-
-  public final void setName(String name) {
-    thread.setName(name);
-  }
-
-  public final void setPriority(int newPriority) {
-    thread.setPriority(newPriority);
-  }
-
-  public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
-    thread.setUncaughtExceptionHandler(eh);
-  }
-
-  public void start() {
-    thread.start();
-  }
-  
-  public final void join() throws InterruptedException {
-    thread.join();
-  }
-
-  public final void join(long millis, int nanos) throws InterruptedException {
-    thread.join(millis, nanos);
-  }
-
-  public final void join(long millis) throws InterruptedException {
-    thread.join(millis);
-  }
-  //// End delegation to Thread
-}

http://git-wip-us.apache.org/repos/asf/hbase/blob/133c79a3/hbase-client/src/main/java/org/apache/hadoop/hbase/util/Sleeper.java
----------------------------------------------------------------------
diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/util/Sleeper.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/util/Sleeper.java
deleted file mode 100644
index 071250b..0000000
--- a/hbase-client/src/main/java/org/apache/hadoop/hbase/util/Sleeper.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/**
- *
- * 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.classification.InterfaceAudience;
-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).
- */
-@InterfaceAudience.Private
-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.notifyAll();
-    }
-  }
-
-  /**
-   * 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://hbase.apache.org/book.html#trouble.rs.runtime.zkexpired");
-        }
-      } 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);
-    }
-    synchronized(sleepLock) {
-      triggerWake = false;
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/hbase/blob/133c79a3/hbase-common/src/main/java/org/apache/hadoop/hbase/Chore.java
----------------------------------------------------------------------
diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/Chore.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/Chore.java
new file mode 100644
index 0000000..c2c7964
--- /dev/null
+++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/Chore.java
@@ -0,0 +1,142 @@
+/**
+ *
+ * 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;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.hbase.classification.InterfaceAudience;
+import org.apache.hadoop.hbase.util.HasThread;
+import org.apache.hadoop.hbase.util.Sleeper;
+
+/**
+ * Chore is a task performed on a period in hbase.  The chore is run in its own
+ * thread. This base abstract class provides while loop and sleeping facility.
+ * If an unhandled exception, the threads exit is logged.
+ * Implementers just need to add checking if there is work to be done and if
+ * so, do it.  Its the base of most of the chore threads in hbase.
+ *
+ * <p>Don't subclass Chore if the task relies on being woken up for something to
+ * do, such as an entry being added to a queue, etc.
+ */
+@InterfaceAudience.Private
+public abstract class Chore extends HasThread {
+  private final Log LOG = LogFactory.getLog(this.getClass());
+  private final Sleeper sleeper;
+  protected final Stoppable stopper;
+
+  /**
+   * @param p Period at which we should run.  Will be adjusted appropriately
+   * should we find work and it takes time to complete.
+   * @param stopper When {@link Stoppable#isStopped()} is true, this thread will
+   * cleanup and exit cleanly.
+   */
+  public Chore(String name, final int p, final Stoppable stopper) {
+    super(name);
+    if (stopper == null){
+      throw new NullPointerException("stopper cannot be null");
+    }
+    this.sleeper = new Sleeper(p, stopper);
+    this.stopper = stopper;
+  }
+
+  /**
+   * This constructor is for test only. It allows to create an object and to call chore() on
+   *  it. There is no sleeper nor stoppable.
+   */
+  protected Chore(){
+    sleeper = null;
+    stopper = null;
+  }
+
+  /**
+   * @see java.lang.Thread#run()
+   */
+  @Override
+  public void run() {
+    try {
+      boolean initialChoreComplete = false;
+      while (!this.stopper.isStopped()) {
+        long startTime = System.currentTimeMillis();
+        try {
+          if (!initialChoreComplete) {
+            initialChoreComplete = initialChore();
+          } else {
+            chore();
+          }
+        } catch (Exception e) {
+          LOG.error("Caught exception", e);
+          if (this.stopper.isStopped()) {
+            continue;
+          }
+        }
+        this.sleeper.sleep(startTime);
+      }
+    } catch (Throwable t) {
+      LOG.fatal(getName() + "error", t);
+    } finally {
+      LOG.info(getName() + " exiting");
+      cleanup();
+    }
+  }
+
+  /**
+   * If the thread is currently sleeping, trigger the core to happen immediately.
+   * If it's in the middle of its operation, will begin another operation
+   * immediately after finishing this one.
+   */
+  public void triggerNow() {
+    this.sleeper.skipSleepCycle();
+  }
+
+  /*
+   * Exposed for TESTING!
+   * calls directly the chore method, from the current thread.
+   */
+  public void choreForTesting() {
+    chore();
+  }
+
+  /**
+   * Override to run a task before we start looping.
+   * @return true if initial chore was successful
+   */
+  protected boolean initialChore() {
+    // Default does nothing.
+    return true;
+  }
+
+  /**
+   * Look for chores.  If any found, do them else just return.
+   */
+  protected abstract void chore();
+
+  /**
+   * Sleep for period.
+   */
+  protected void sleep() {
+    this.sleeper.sleep();
+  }
+
+  /**
+   * Called when the chore has completed, allowing subclasses to cleanup any
+   * extra overhead
+   */
+  protected void cleanup() {
+  }
+}

http://git-wip-us.apache.org/repos/asf/hbase/blob/133c79a3/hbase-common/src/main/java/org/apache/hadoop/hbase/Stoppable.java
----------------------------------------------------------------------
diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/Stoppable.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/Stoppable.java
new file mode 100644
index 0000000..b87b764
--- /dev/null
+++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/Stoppable.java
@@ -0,0 +1,38 @@
+/**
+ *
+ * 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;
+
+import org.apache.hadoop.hbase.classification.InterfaceAudience;
+
+/**
+ * Implementers are Stoppable.
+ */
+@InterfaceAudience.Private
+public interface Stoppable {
+  /**
+   * Stop this service.
+   * @param why Why we're stopping.
+   */
+  void stop(String why);
+
+  /**
+   * @return True if {@link #stop(String)} has been closed.
+   */
+  boolean isStopped();
+}

http://git-wip-us.apache.org/repos/asf/hbase/blob/133c79a3/hbase-common/src/main/java/org/apache/hadoop/hbase/util/HasThread.java
----------------------------------------------------------------------
diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/util/HasThread.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/util/HasThread.java
new file mode 100644
index 0000000..4457fe0
--- /dev/null
+++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/util/HasThread.java
@@ -0,0 +1,100 @@
+/**
+ * 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.hadoop.hbase.classification.InterfaceAudience;
+
+import java.lang.Thread.UncaughtExceptionHandler;
+
+/**
+ * Abstract class which contains a Thread and delegates the common Thread
+ * methods to that instance.
+ * 
+ * The purpose of this class is to workaround Sun JVM bug #6915621, in which
+ * something internal to the JDK uses Thread.currentThread() as a monitor
+ * lock. This can produce deadlocks like HBASE-4367, HBASE-4101, etc.
+ */
+@InterfaceAudience.Private
+public abstract class HasThread implements Runnable {
+  private final Thread thread;
+  
+  public HasThread() {
+    this.thread = new Thread(this);
+  }
+
+  public HasThread(String name) {
+    this.thread = new Thread(this, name);
+  }
+  
+  public Thread getThread() {
+    return thread;
+  }
+  
+  public abstract void run();
+  
+  //// Begin delegation to Thread
+  
+  public final String getName() {
+    return thread.getName();
+  }
+
+  public void interrupt() {
+    thread.interrupt();
+  }
+
+  public final boolean isAlive() {
+    return thread.isAlive();
+  }
+
+  public boolean isInterrupted() {
+    return thread.isInterrupted();
+  }
+
+  public final void setDaemon(boolean on) {
+    thread.setDaemon(on);
+  }
+
+  public final void setName(String name) {
+    thread.setName(name);
+  }
+
+  public final void setPriority(int newPriority) {
+    thread.setPriority(newPriority);
+  }
+
+  public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
+    thread.setUncaughtExceptionHandler(eh);
+  }
+
+  public void start() {
+    thread.start();
+  }
+  
+  public final void join() throws InterruptedException {
+    thread.join();
+  }
+
+  public final void join(long millis, int nanos) throws InterruptedException {
+    thread.join(millis, nanos);
+  }
+
+  public final void join(long millis) throws InterruptedException {
+    thread.join(millis);
+  }
+  //// End delegation to Thread
+}

http://git-wip-us.apache.org/repos/asf/hbase/blob/133c79a3/hbase-common/src/main/java/org/apache/hadoop/hbase/util/Sleeper.java
----------------------------------------------------------------------
diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/util/Sleeper.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/util/Sleeper.java
new file mode 100644
index 0000000..071250b
--- /dev/null
+++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/util/Sleeper.java
@@ -0,0 +1,117 @@
+/**
+ *
+ * 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.classification.InterfaceAudience;
+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).
+ */
+@InterfaceAudience.Private
+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.notifyAll();
+    }
+  }
+
+  /**
+   * 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://hbase.apache.org/book.html#trouble.rs.runtime.zkexpired");
+        }
+      } 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);
+    }
+    synchronized(sleepLock) {
+      triggerWake = false;
+    }
+  }
+}