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

com.appland.appmap.process.ThreadLock Maven / Gradle / Ivy

package com.appland.appmap.process;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Stack;

/**
 * Single-threaded locking mechanisms. This class provides behavior to restrict hooks within a
 * single thread from running while another hook is already in progress.
 */
public class ThreadLock {
  private class ThreadLockStatus {
    public Boolean globalLock = false;
    public HashSet uniqueKeys;

    ThreadLockStatus() { }

    public boolean addUniqueKey(String key) {
      if (this.uniqueKeys == null) {
        this.uniqueKeys = new HashSet();
      }

      return this.uniqueKeys.add(key);
    }

    public boolean contains(String key) {
      if (this.uniqueKeys == null) {
        return false;
      }

      return this.uniqueKeys.contains(key);
    }

    public boolean hasGlobalLock() {
      return this.globalLock;
    }

    public void setGlobalLock(Boolean globalLock) {
      this.globalLock = globalLock;
    }
  }

  private static final HashMap instances =
      new HashMap();

  private final Stack statusStack = new Stack();

  private ThreadLock() { }

  /**
   * Get the ThreadLock instance for this thread.
   * @return A ThreadLock instance unique to the current thread
   */
  public static ThreadLock current() {
    Thread currentThread = Thread.currentThread();
    ThreadLock instance = null;

    instance = ThreadLock.instances.get(currentThread);
    if (instance == null) {
      instance = new ThreadLock();
      ThreadLock.instances.put(currentThread, instance);
    }

    return instance;
  }

  /**
   * Checks if any execution context holds the global lock.
   * @return {@code true} if the global lock is locked. Otherwise, {@code false}.
   */
  public boolean isLocked() {
    for (ThreadLockStatus status : this.statusStack) {
      if (status.hasGlobalLock()) {
        return true;
      }
    }

    return false;
  }

  /**
   * Checks if the current execution context is globally locked.
   * @return {@code true} if the global lock is locked. Otherwise, {@code false}.
   */
  public boolean hasLock() {
    if (this.statusStack.isEmpty()) {
      return false;
    }

    return this.statusStack.peek().hasGlobalLock();
  }

  /**
   * Checks if the current execution context holds a lock on a unique key.
   * @param key The unique key to check lock status on
   * @return {@code true} if the global lock is locked. Otherwise, {@code false}.
   */
  public boolean hasUniqueLock(String key) {
    if (this.statusStack.isEmpty()) {
      return false;
    }

    return this.statusStack.peek().contains(key);
  }

  /**
   * Pushes a new execution context on the stack. This should be called when first entering a
   * method, before acquiring any locks.
   * @return {@code true} if the global lock is locked. Otherwise, {@code false}.
   * @see ThreadLock#exit
   */
  public void enter() {
    this.statusStack.push(new ThreadLockStatus());
  }

  /**
   * Pops an execution context off the stack. This should be called just before exiting a method.
   * Releases any existing locks held.
   * @see ThreadLock#enter
   */
  public void exit() {
    if (this.statusStack.isEmpty()) {
      return;
    }

    this.statusStack.pop();
  }

  /**
   * Attempts to acquire a lock on a unique key for the current execution context.
   * @return {@code true} if the unique lock was acquired. Otherwise, {@code false}.
   */
  public boolean lockUnique(String key) {
    if (this.statusStack.isEmpty()) {
      return false;
    }

    for (ThreadLockStatus status : this.statusStack) {
      if (status.contains(key)) {
        return false;
      }
    }

    return this.statusStack.peek().addUniqueKey(key);
  }

  /**
   * Attempts to acquire the global lock as the current execution context.
   * @return {@code true} if the global lock was acquired. Otherwise, {@code false}.
   */
  public boolean lock() {
    if (this.isLocked()) {
      return false;
    }

    if (this.statusStack.isEmpty()) {
      return false;
    }

    this.statusStack.peek().setGlobalLock(true);
    return true;
  }

  /**
   * Attempts to release a global lock on the current execution context.
   * @return {@code true} if the lock was successfully released. Otherwise, {@code false}.
   */
  public boolean unlock() {
    if (this.statusStack.isEmpty()) {
      return false;
    }

    final ThreadLockStatus top = this.statusStack.peek();
    if (!top.hasGlobalLock()) {
      return false;
    }

    top.setGlobalLock(false);
    return true;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy