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

org.apache.druid.concurrent.LifecycleLock Maven / Gradle / Ivy

There is a newer version: 31.0.0
Show newest version
/*
 * 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.druid.concurrent;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;

/**
 * A synchronization tool for lifecycled objects (see {@link org.apache.druid.java.util.common.lifecycle.Lifecycle},
 * that need happens-before between start() and other methods and/or to check that the object was successfully started
 * in other methods.
 *
 * Guarantees in terms of JMM: happens-before between {@link #exitStart()} and {@link #awaitStarted()},
 * exitStart() and {@link #canStop()}, if it returns {@code true}.
 *
 * Example:
 * class ExampleLifecycledClass
 * {
 *   final LifecycleLock lifecycleLock = new LifecycleLock();
 *
 *   void start()
 *   {
 *     if (!lifecycleLock.canStart()) {
 *       .. return or throw exception
 *     }
 *     try {
 *       .. do start
 *       lifecycleLock.started();
 *     }
 *     finally {
 *       lifecycleLock.exitStart();
 *     }
 *   }
 *
 *   void otherMethod()
 *   {
 *     Preconditions.checkState(lifecycleLock.awaitStarted());
 *     // all actions done in start() are visible here
 *     .. do stuff
 *   }
 *
 *   void stop()
 *   {
 *     if (!lifecycleLock.canStop()) {
 *       .. return or throw exception
 *     }
 *     // all actions done in start() are visible here
 *     .. do stop
 *   }
 * }
 */
public class LifecycleLock
{
  private static class Sync extends AbstractQueuedSynchronizer
  {
    private static final int NOT_STARTED = 0;
    private static final int STARTING = 1;
    private static final int STARTED = 2;
    private static final int START_EXITED_SUCCESSFUL = 3;
    private static final int START_EXITED_FAIL = 4;
    private static final int STOPPING = 5;
    private static final int STOPPED = 6;

    boolean canStart()
    {
      return compareAndSetState(NOT_STARTED, STARTING);
    }

    void started()
    {
      if (!compareAndSetState(STARTING, STARTED)) {
        throw new IllegalMonitorStateException("Called started() not in the context of start()");
      }
    }

    void exitStart()
    {
      // see tryReleaseShared()
      releaseShared(1);
    }

    @Override
    protected boolean tryReleaseShared(int ignore)
    {
      while (true) {
        int state = getState();
        if (state == STARTING) {
          if (compareAndSetState(STARTING, START_EXITED_FAIL)) {
            return true;
          }
        } else if (state == STARTED) {
          if (compareAndSetState(STARTED, START_EXITED_SUCCESSFUL)) {
            return true;
          }
        } else {
          throw new IllegalMonitorStateException("exitStart() called not in the end of the start() method");
        }
      }
    }

    boolean isStarted()
    {
      return getState() == START_EXITED_SUCCESSFUL;
    }

    boolean awaitStarted()
    {
      try {
        // see tryAcquireShared()
        acquireSharedInterruptibly(1);
      }
      catch (InterruptedException e) {
        throw new RuntimeException(e);
      }
      return isStarted();
    }

    boolean awaitStarted(long timeNanos)
    {
      try {
        // see tryAcquireShared()
        if (!tryAcquireSharedNanos(1, timeNanos)) {
          return false;
        }
      }
      catch (InterruptedException e) {
        throw new RuntimeException(e);
      }
      return isStarted();
    }

    @Override
    protected int tryAcquireShared(int ignore)
    {
      return getState() > STARTED ? 1 : -1;
    }

    boolean canStop()
    {
      while (true) {
        int state = getState();
        if (state == START_EXITED_FAIL || state == STOPPING) {
          return false;
        } else if (state == START_EXITED_SUCCESSFUL) {
          if (compareAndSetState(START_EXITED_SUCCESSFUL, STOPPING)) {
            return true;
          }
        } else {
          throw new IllegalMonitorStateException("Called canStop() before start()");
        }
      }
    }

    void exitStop()
    {
      if (!compareAndSetState(STOPPING, STOPPED)) {
        throw new IllegalMonitorStateException("Called exitStop() not in the context of stop()");
      }
    }

    void exitStopAndReset()
    {
      if (!compareAndSetState(STOPPING, NOT_STARTED)) {
        throw new IllegalMonitorStateException("Not called exitStop() before reset()");
      }
    }
  }

  private final Sync sync = new Sync();

  /**
   * Start latch, only one canStart() call in any thread on this LifecycleLock object could return true, if {@link
   * #exitStopAndReset()} is not called in between.
   */
  public boolean canStart()
  {
    return sync.canStart();
  }

  /**
   * Announce the start was successful.
   *
   * @throws IllegalMonitorStateException if {@link #canStart()} is not yet called or if {@link #exitStart()} is already
   * called on this LifecycleLock
   */
  public void started()
  {
    sync.started();
  }

  /**
   * Must be called before exit from start() on the lifecycled object, usually in a finally block.
   *
   * @throws IllegalMonitorStateException if {@link #canStart()} is not yet called on this LifecycleLock
   */
  public void exitStart()
  {
    sync.exitStart();
  }

  /**
   * Returns {@code true} if {@link #started()} was called before that. Returns {@code false} if {@link #started()} is
   * not called before {@link #exitStart()}, or if {@link #canStop()} is already called on this LifecycleLock.
   */
  public boolean isStarted()
  {
    return sync.isStarted();
  }

  /**
   * Awaits until {@link #exitStart()} is called, if needed, and returns {@code true} if {@link #started()} was called
   * before that. Returns {@code false} if {@link #started()} is not called before {@link #exitStart()}, or if {@link
   * #canStop()} is already called on this LifecycleLock.
   */
  public boolean awaitStarted()
  {
    return sync.awaitStarted();
  }

  /**
   * Awaits until {@link #exitStart()} is called for at most the specified timeout, and returns {@code true} if {@link
   * #started()} was called before that. Returns {@code false} if {@code started()} wasn't called before {@code
   * exitStart()}, or if {@code exitStart()} isn't called on this LifecycleLock until the specified timeout expires, or
   * if {@link #canStop()} is already called on this LifecycleLock.
   */
  public boolean awaitStarted(long timeout, TimeUnit unit)
  {
    return sync.awaitStarted(unit.toNanos(timeout));
  }

  /**
   * Stop latch, only one canStop() call in any thread on this LifecycleLock object could return {@code true}. If
   * {@link #started()} wasn't called on this LifecycleLock object, always returns {@code false}.
   *
   * @throws IllegalMonitorStateException if {@link #exitStart()} are not yet called on this LifecycleLock
   */
  public boolean canStop()
  {
    return sync.canStop();
  }

  /**
   * Finalizes stopping the LifecycleLock. This method must be called before exit from stop() on this object,
   * usually in a finally block. If you're using a restartable object, use {@link #exitStopAndReset()} instead.
   *
   * @throws IllegalMonitorStateException if {@link #canStop()} is not yet called on this LifecycleLock
   */
  public void exitStop()
  {
    sync.exitStop();
  }

  /**
   * Finalizes stopping the LifecycleLock and resets it, so that {@link #canStart()} could be called again. If this
   * LifecycleLock is used in a restartable object, this method must be called before exit from stop() on this object,
   * usually in a finally block.
   *
   * @throws IllegalMonitorStateException if {@link #canStop()} is not yet called on this LifecycleLock
   */
  public void exitStopAndReset()
  {
    sync.exitStopAndReset();
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy