content: /*
   * The Alluxio Open Foundation licenses this work under the Apache License, version 2.0
   * (the "License"). You may not use this work except in compliance with the License, which is
   * available at www.apache.org/licenses/LICENSE-2.0
   *
   * This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
   * either express or implied, as more fully set forth in the License.
   *
   * See the NOTICE file distributed with this work for information regarding copyright ownership.
   */
  
  package alluxio.master.file.meta;
  
  import alluxio.concurrent.LockMode;
  
  import java.util.ArrayList;
  import java.util.List;
  import javax.annotation.concurrent.NotThreadSafe;
  
  /**
   * Represents a locked path within the inode tree. Both inodes and edges are locked along the path.
   * The path may start from either an edge or an inode.
   *
   * A lock list always contains some number of read locks (possibly zero) followed by some number of
   * write locks (possibly zero).
   *
   * This class uses list notation to describe lock lists. Nodes are single letters, edges are
   * parentName->childName, and write-locks are indicated with '*'. For example, a lock list for
   * /a/b/c/d where the c->d edge is the first write-locked element:
   *
   * [->/, /, /->a, a, a->b, b, b->c, c, c->d*, d*]
   *
   * The "->/" at the start is a pseudo-edge used to allow the WRITE_EDGE lock mode to be applied to
   * the root. This edge can only be locked by calling lockRootEdge.
   *
   * Not all lock lists need to start from the root. Another example list could be
   *
   * [b, b->c, c->d*, d*]
   *
   * This allows us to create composite lock lists out of a "normal" lock list starting from the root,
   * plus a non-root lock list.
   */
  @NotThreadSafe
  public interface InodeLockList extends AutoCloseable {
    /**
     * Locks the root edge in the specified mode.
     *
     * The lock list must be empty to call this method.
     *
     * @param mode the mode to lock in
     */
    void lockRootEdge(LockMode mode);
  
    /**
     * Locks the given inode and adds it to the lock list. This method does *not* check that the inode
     * is still a child of the previous inode, or that the inode still exists. This method should only
     * be called when the edge leading to the inode is locked.
     *
     * Example
     * Starting from [a, a->b]
     *
     * lockInode(b, LockMode.READ) results in [a, a->b, b]
     * lockInode(b, LockMode.WRITE) results in [a, a->b, b*]
     *
     * @param inode the inode to lock
     * @param mode the mode to lock in
     */
    void lockInode(Inode inode, LockMode mode);
  
    /**
     * Locks an edge leading out of the last inode in the list.
     *
     * Example
     * Starting from [a, a->b, b]
     *
     * lockEdge(b, c, LockMode.READ) results in [a, a->b, b, b->c]
     * lockEdge(b, c, LockMode.WRITE) results in [a, a->b, b, b->c*]
     *
     * @param inode the parent inode of the edge
     * @param childName the child name of the edge
     * @param mode the mode to lock in
     */
    void lockEdge(Inode inode, String childName, LockMode mode);
  
    /**
     * Unlocks the last locked inode.
     *
     * Example
     * Starting from [a, a->b, b]
     *
     * unlockLastInode() results in [a, a->b]
     */
    void unlockLastInode();
  
    /**
     * Unlocks the last locked edge.
     *
     * Example
     * Starting from [a, a->b]
     *
     * unlockLastEdge results in [a]
     */
    void unlockLastEdge();
  
    /**
     * Downgrades all locks in the current lock list to read locks.
     */
    void downgradeToReadLocks();
  
    /**
     * Downgrades the last edge lock in the lock list from WRITE lock to READ lock.
     *
     * Example
     * Starting from [a, a->b*]
     *
     * downgradeLastEdge() results in [a, a->b]
     */
    void downgradeLastEdge();
  
    /**
     * Leapfrogs the final edge write lock forward, reducing the lock list's write-locked scope.
     *
     * Example
     * Starting from [a, a->b*]
     *
     * pushWriteLockedEdge(b, c) results in [a, a->b, b, b->c*]
     *
     * The read lock on a->b is acquired before releasing the write lock. This ensures that no other
     * thread can take the write lock before the read lock is acquired.
     *
     * If this is a composite lock list and the final write lock is part of the base lock list, the
     * new locks will be acquired but no downgrade will occur.
     *
     * @param inode the inode to add to the lock list
     * @param childName the child name for the edge to add to the lock list
     */
    void pushWriteLockedEdge(Inode inode, String childName);
  
    /**
     * @return {@link LockMode#WRITE} if the last entry in the list is write-locked, otherwise
     *         {@link LockMode#READ}
     */
    LockMode getLockMode();
  
    /**
     * @return a copy of all locked inodes
     */
    List<Inode> getLockedInodes();
  
    /**
     * @return a copy of all locked inodes
     */
    default List<InodeView> getLockedInodeViews() {
      return new ArrayList<>(getLockedInodes());
    }
  
    /**
     * @param index the index of the list
     * @return the inode at the specified index
     */
    Inode get(int index);
  
    /**
     * @return the size of the list in terms of locked inodes
     */
    int numInodes();
  
    /**
     * @return whether this lock list ends in an inode (as opposed to an edge)
     */
    boolean endsInInode();
  
    /**
     * @return true if the locklist is empty
     */
    boolean isEmpty();
  
    /**
     * @return the inode lock manager for this lock list
     */
    InodeLockManager getInodeLockManager();
  
    /**
     * Closes the lock list, releasing all locks.
     */
    @Override
    void close();
  }
  