/*
 * 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 threaddemo.locking;

// XXX add support for read/writeSoon(Runnable)?
// XXX why bother with InvocationTargetException?

/**
 * Some kind of a lock, with support for possible read and write modes.
 * <P>
 * Examples of use:
 *
 * <pre>
 * Lock m = Locks.readWriteLock("foo", 0);
 *
 * // Grant write access, compute an integer and return it:
 * return m.write(new LockAction<Integer>() {
 *     public Integer run() {
 *         return 1;
 *     }
 * });
 *
 * // Obtain read access, do some computation, possibly throw an IOException:
 * m.read(new LockExceptionAction<Void,IOException>() {
 *     public Void run() throws IOException {
 *         if (...) throw new IOException();
 *         return null;
 *     }
 * });
 * </pre>
 *
 * @see Locks
 * @author Jesse Glick
 */
public interface RWLock {
    
    /** Run an action only with read access.
    * @param action the action to perform
    * @return the object returned from {@link LockAction#run}
    */
    <T> T read(LockAction<T> action);

    /** Run an action with read access and possibly throw a checked exception.
    * Note that <em>runtime exceptions</em> are always passed through, and neither
    * require this invocation style, nor are encapsulated.
    * @param action the action to execute
    * @return the object returned from {@link LockExceptionAction#run}
    * @throws E a checked exception, if any
    * @throws RuntimeException if any runtime exception is thrown from the run method
    * @see #read(LockAction)
    */
    <T, E extends Exception> T read(LockExceptionAction<T, E> action) throws E;

    /** Run an action with read access, returning no result.
    * @param action the action to perform
    * @see #read(LockAction)
    */
    void read(Runnable action);
    
    /**
     * Run an action asynch with read access.
    * @param action the action to perform
    * @see #read(Runnable)
    */
    void readLater(Runnable action);

    /**
     * Run an action with write access.
     * <p><strong>May not be called while holding read access.</strong>
     * @param action the action to perform
     * @return the result of {@link LockAction#run}
     */
    <T> T write(LockAction<T> action);

    /**
     * Run an action with write access and possibly throw an exception.
     * <p><strong>May not be called while holding read access.</strong>
     * @param action the action to execute
     * @return the result of {@link LockExceptionAction#run}
     * @throws E a checked exception, if any
     * @throws RuntimeException if a runtime exception is thrown in the action
     * @see #write(LockAction)
     * @see #read(LockExceptionAction)
     */
    <T, E extends Exception> T write(LockExceptionAction<T, E> action) throws E;

    /**
     * Run an action with write access and return no result.
     * @param action the action to perform
     * @see #write(LockAction)
     * @see #read(Runnable)
     */
    void write(Runnable action);
    
    /**
     * Run an action asynchronously with write access.
     * @param action the action to perform
     * @see #write(LockAction)
     * @see #readLater(Runnable)
     */
    void writeLater(Runnable action);

    /**
     * Check if the current thread is holding a read or write lock.
     * @return true if either read or write access is available
     */
    boolean canRead();

    /**
     * Check if the current thread is holding the write lock.
     * Note that this will be false in case write access was entered and then
     * read access was entered inside of that, until the nested read access is
     * again exited.
     * @return true if write access is available
     */
    boolean canWrite();

}
