All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.tencent.angel.utils.Sleeper Maven / Gradle / Ivy

The newest version!
/*
 * Tencent is pleased to support the open source community by making Angel available.
 *
 * Copyright (C) 2017-2018 THL A29 Limited, a Tencent company. All rights reserved.
 *
 * Licensed 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
 *
 * https://opensource.org/licenses/Apache-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 com.tencent.angel.utils;

import com.tencent.angel.Stoppable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 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).
 */
public class Sleeper {
  private final Logger LOG = LoggerFactory.getLogger(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 volatile 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 startTime
   *
   * @param startTime Time some task started previous to now. Time to sleep will be docked current
   *                  time minus passed startTime.
   */
  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);
    }
    triggerWake = false;
  }
}