    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.lang3.concurrent.locks;
    018
    019import java.util.Objects;
    020import java.util.concurrent.locks.Lock;
    021import java.util.concurrent.locks.ReadWriteLock;
    022import java.util.concurrent.locks.ReentrantReadWriteLock;
    023import java.util.concurrent.locks.StampedLock;
    024import java.util.function.Supplier;
    025
    026import org.apache.commons.lang3.function.Failable;
    027import org.apache.commons.lang3.function.FailableConsumer;
    028import org.apache.commons.lang3.function.FailableFunction;
    029
    030/**
    031 * Combines the monitor and visitor pattern to work with {@link java.util.concurrent.locks.Lock locked objects}. Locked
    032 * objects are an alternative to synchronization. This, on Wikipedia, is known as the Visitor pattern
    033 * (https://en.wikipedia.org/wiki/Visitor_pattern), and from the "Gang of Four" "Design Patterns" book's Visitor pattern
    034 * [Gamma, E., Helm, R., &amp; Johnson, R. (1998). Visitor. In Design patterns elements of reusable object oriented software (pp. 331-344). Reading: Addison Wesley.].
    035 *
    036 * <p>
    037 * Locking is preferable, if there is a distinction between read access (multiple threads may have read access
    038 * concurrently), and write access (only one thread may have write access at any given time). In comparison,
    039 * synchronization doesn't support read access, because synchronized access is exclusive.
    040 * </p>
    041 * <p>
    042 * Using this class is fairly straightforward:
    043 * </p>
    044 * <ol>
    045 * <li>While still in single thread mode, create an instance of {@link LockingVisitors.StampedLockVisitor} by calling
    046 * {@link #stampedLockVisitor(Object)}, passing the object which needs to be locked. Discard all references to the
    047 * locked object. Instead, use references to the lock.</li>
    048 * <li>If you want to access the locked object, create a {@link FailableConsumer}. The consumer will receive the locked
    049 * object as a parameter. For convenience, the consumer may be implemented as a Lambda. Then invoke
    050 * {@link LockingVisitors.StampedLockVisitor#acceptReadLocked(FailableConsumer)}, or
    051 * {@link LockingVisitors.StampedLockVisitor#acceptWriteLocked(FailableConsumer)}, passing the consumer.</li>
    052 * <li>As an alternative, if you need to produce a result object, you may use a {@link FailableFunction}. This function
    053 * may also be implemented as a Lambda. To have the function executed, invoke
    054 * {@link LockingVisitors.StampedLockVisitor#applyReadLocked(FailableFunction)}, or
    055 * {@link LockingVisitors.StampedLockVisitor#applyWriteLocked(FailableFunction)}.</li>
    056 * </ol>
    057 * <p>
    058 * Example: A thread safe logger class.
    059 * </p>
    060 *
    061 * <pre>
    062 *   public class SimpleLogger {
    063 *
    064 *     private final StampedLockVisitor&lt;PrintStream&gt; lock;
    065 *
    066 *     public SimpleLogger(OutputStream out) {
    067 *         lock = LockingVisitors.stampedLockVisitor(new PrintStream(out));
    068 *     }
    069 *
    070 *     public void log(String message) {
    071 *         lock.acceptWriteLocked((ps) -&gt; ps.println(message));
    072 *     }
    073 *
    074 *     public void log(byte[] buffer) {
    075 *         lock.acceptWriteLocked((ps) -&gt; { ps.write(buffer); ps.println(); });
    076 *     }
    077 * </pre>
    078 *
    079 * @since 3.11
    080 */
    081public class LockingVisitors {
    082
    083    /**
    084     * Wraps a domain object and a lock for access by lambdas.
    085     *
    086     * @param <O> the wrapped object type.
    087     * @param <L> the wrapped lock type.
    088     */
    089    public static class LockVisitor<O, L> {
    090
    091        /**
    092         * The lock object, untyped, since, for example {@link StampedLock} does not implement a locking interface in
    093         * Java 8.
    094         */
    095        private final L lock;
    096
    097        /**
    098         * The guarded object.
    099         */
    100        private final O object;
    101
    102        /**
    103         * Supplies the read lock, usually from the lock object.
    104         */
    105        private final Supplier<Lock> readLockSupplier;
    106
    107        /**
    108         * Supplies the write lock, usually from the lock object.
    109         */
    110        private final Supplier<Lock> writeLockSupplier;
    111
    112        /**
    113         * Constructs an instance.
    114         *
    115         * @param object The object to guard.
    116         * @param lock The locking object.
    117         * @param readLockSupplier Supplies the read lock, usually from the lock object.
    118         * @param writeLockSupplier Supplies the write lock, usually from the lock object.
    119         */
    120        protected LockVisitor(final O object, final L lock, final Supplier<Lock> readLockSupplier, final Supplier<Lock> writeLockSupplier) {
    121            this.object = Objects.requireNonNull(object, "object");
    122            this.lock = Objects.requireNonNull(lock, "lock");
    123            this.readLockSupplier = Objects.requireNonNull(readLockSupplier, "readLockSupplier");
    124            this.writeLockSupplier = Objects.requireNonNull(writeLockSupplier, "writeLockSupplier");
    125        }
    126
    127        /**
    128         * Provides read (shared, non-exclusive) access to the locked (hidden) object. More precisely, what the method
    129         * will do (in the given order):
    130         *
    131         * <ol>
    132         * <li>Obtain a read (shared) lock on the locked (hidden) object. The current thread may block, until such a
    133         * lock is granted.</li>
    134         * <li>Invokes the given {@link FailableConsumer consumer}, passing the locked object as the parameter.</li>
    135         * <li>Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the
    136         * lock will be released anyways.</li>
    137         * </ol>
    138         *
    139         * @param consumer The consumer, which is being invoked to use the hidden object, which will be passed as the
    140         *        consumers parameter.
    141         * @see #acceptWriteLocked(FailableConsumer)
    142         * @see #applyReadLocked(FailableFunction)
    143         */
    144        public void acceptReadLocked(final FailableConsumer<O, ?> consumer) {
    145            lockAcceptUnlock(readLockSupplier, consumer);
    146        }
    147
    148        /**
    149         * Provides write (exclusive) access to the locked (hidden) object. More precisely, what the method will do (in
    150         * the given order):
    151         *
    152         * <ol>
    153         * <li>Obtain a write (shared) lock on the locked (hidden) object. The current thread may block, until such a
    154         * lock is granted.</li>
    155         * <li>Invokes the given {@link FailableConsumer consumer}, passing the locked object as the parameter.</li>
    156         * <li>Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the
    157         * lock will be released anyways.</li>
    158         * </ol>
    159         *
    160         * @param consumer The consumer, which is being invoked to use the hidden object, which will be passed as the
    161         *        consumers parameter.
    162         * @see #acceptReadLocked(FailableConsumer)
    163         * @see #applyWriteLocked(FailableFunction)
    164         */
    165        public void acceptWriteLocked(final FailableConsumer<O, ?> consumer) {
    166            lockAcceptUnlock(writeLockSupplier, consumer);
    167        }
    168
    169        /**
    170         * Provides read (shared, non-exclusive) access to the locked (hidden) object for the purpose of computing a
    171         * result object. More precisely, what the method will do (in the given order):
    172         *
    173         * <ol>
    174         * <li>Obtain a read (shared) lock on the locked (hidden) object. The current thread may block, until such a
    175         * lock is granted.</li>
    176         * <li>Invokes the given {@link FailableFunction function}, passing the locked object as the parameter,
    177         * receiving the functions result.</li>
    178         * <li>Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the
    179         * lock will be released anyways.</li>
    180         * <li>Return the result object, that has been received from the functions invocation.</li>
    181         * </ol>
    182         * <p>
    183         * <em>Example:</em> Consider that the hidden object is a list, and we wish to know the current size of the
    184         * list. This might be achieved with the following:
    185         * </p>
    186         * <pre>
    187         * private Lock&lt;List&lt;Object&gt;&gt; listLock;
    188         *
    189         * public int getCurrentListSize() {
    190         *     final Integer sizeInteger = listLock.applyReadLocked((list) -&gt; Integer.valueOf(list.size));
    191         *     return sizeInteger.intValue();
    192         * }
    193         * </pre>
    194         *
    195         * @param <T> The result type (both the functions, and this method's.)
    196         * @param function The function, which is being invoked to compute the result. The function will receive the
    197         *        hidden object.
    198         * @return The result object, which has been returned by the functions invocation.
    199         * @throws IllegalStateException The result object would be, in fact, the hidden object. This would extend
    200         *         access to the hidden object beyond this methods lifetime and will therefore be prevented.
    201         * @see #acceptReadLocked(FailableConsumer)
    202         * @see #applyWriteLocked(FailableFunction)
    203         */
    204        public <T> T applyReadLocked(final FailableFunction<O, T, ?> function) {
    205            return lockApplyUnlock(readLockSupplier, function);
    206        }
    207
    208        /**
    209         * Provides write (exclusive) access to the locked (hidden) object for the purpose of computing a result object.
    210         * More precisely, what the method will do (in the given order):
    211         *
    212         * <ol>
    213         * <li>Obtain a read (shared) lock on the locked (hidden) object. The current thread may block, until such a
    214         * lock is granted.</li>
    215         * <li>Invokes the given {@link FailableFunction function}, passing the locked object as the parameter,
    216         * receiving the functions result.</li>
    217         * <li>Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the
    218         * lock will be released anyways.</li>
    219         * <li>Return the result object, that has been received from the functions invocation.</li>
    220         * </ol>
    221         *
    222         * @param <T> The result type (both the functions, and this method's.)
    223         * @param function The function, which is being invoked to compute the result. The function will receive the
    224         *        hidden object.
    225         * @return The result object, which has been returned by the functions invocation.
    226         * @throws IllegalStateException The result object would be, in fact, the hidden object. This would extend
    227         *         access to the hidden object beyond this methods lifetime and will therefore be prevented.
    228         * @see #acceptReadLocked(FailableConsumer)
    229         * @see #applyWriteLocked(FailableFunction)
    230         */
    231        public <T> T applyWriteLocked(final FailableFunction<O, T, ?> function) {
    232            return lockApplyUnlock(writeLockSupplier, function);
    233        }
    234
    235        /**
    236         * Gets the lock.
    237         *
    238         * @return the lock.
    239         */
    240        public L getLock() {
    241            return lock;
    242        }
    243
    244        /**
    245         * Gets the guarded object.
    246         *
    247         * @return the object.
    248         */
    249        public O getObject() {
    250            return object;
    251        }
    252
    253        /**
    254         * This method provides the default implementation for {@link #acceptReadLocked(FailableConsumer)}, and
    255         * {@link #acceptWriteLocked(FailableConsumer)}.
    256         *
    257         * @param lockSupplier A supplier for the lock. (This provides, in fact, a long, because a {@link StampedLock} is used
    258         *        internally.)
    259         * @param consumer The consumer, which is to be given access to the locked (hidden) object, which will be passed
    260         *        as a parameter.
    261         * @see #acceptReadLocked(FailableConsumer)
    262         * @see #acceptWriteLocked(FailableConsumer)
    263         */
    264        protected void lockAcceptUnlock(final Supplier<Lock> lockSupplier, final FailableConsumer<O, ?> consumer) {
    265            final Lock lock = lockSupplier.get();
    266            lock.lock();
    267            try {
    268                consumer.accept(object);
    269            } catch (final Throwable t) {
    270                throw Failable.rethrow(t);
    271            } finally {
    272                lock.unlock();
    273            }
    274        }
    275
    276        /**
    277         * This method provides the actual implementation for {@link #applyReadLocked(FailableFunction)}, and
    278         * {@link #applyWriteLocked(FailableFunction)}.
    279         *
    280         * @param <T> The result type (both the functions, and this method's.)
    281         * @param lockSupplier A supplier for the lock. (This provides, in fact, a long, because a {@link StampedLock} is used
    282         *        internally.)
    283         * @param function The function, which is being invoked to compute the result object. This function will receive
    284         *        the locked (hidden) object as a parameter.
    285         * @return The result object, which has been returned by the functions invocation.
    286         * @throws IllegalStateException The result object would be, in fact, the hidden object. This would extend
    287         *         access to the hidden object beyond this methods lifetime and will therefore be prevented.
    288         * @see #applyReadLocked(FailableFunction)
    289         * @see #applyWriteLocked(FailableFunction)
    290         */
    291        protected <T> T lockApplyUnlock(final Supplier<Lock> lockSupplier, final FailableFunction<O, T, ?> function) {
    292            final Lock lock = lockSupplier.get();
    293            lock.lock();
    294            try {
    295                return function.apply(object);
    296            } catch (final Throwable t) {
    297                throw Failable.rethrow(t);
    298            } finally {
    299                lock.unlock();
    300            }
    301        }
    302
    303    }
    304
    305    /**
    306     * This class implements a wrapper for a locked (hidden) object, and provides the means to access it. The basic
    307     * idea, is that the user code forsakes all references to the locked object, using only the wrapper object, and the
    308     * accessor methods {@link #acceptReadLocked(FailableConsumer)}, {@link #acceptWriteLocked(FailableConsumer)},
    309     * {@link #applyReadLocked(FailableFunction)}, and {@link #applyWriteLocked(FailableFunction)}. By doing so, the
    310     * necessary protections are guaranteed.
    311     *
    312     * @param <O> The locked (hidden) objects type.
    313     */
    314    public static class ReadWriteLockVisitor<O> extends LockVisitor<O, ReadWriteLock> {
    315
    316        /**
    317         * Creates a new instance with the given locked object. This constructor is supposed to be used for subclassing
    318         * only. In general, it is suggested to use {@link LockingVisitors#stampedLockVisitor(Object)} instead.
    319         *
    320         * @param object The locked (hidden) object. The caller is supposed to drop all references to the locked object.
    321         * @param readWriteLock the lock to use.
    322         */
    323        protected ReadWriteLockVisitor(final O object, final ReadWriteLock readWriteLock) {
    324            super(object, readWriteLock, readWriteLock::readLock, readWriteLock::writeLock);
    325        }
    326    }
    327
    328    /**
    329     * This class implements a wrapper for a locked (hidden) object, and provides the means to access it. The basic
    330     * idea is that the user code forsakes all references to the locked object, using only the wrapper object, and the
    331     * accessor methods {@link #acceptReadLocked(FailableConsumer)}, {@link #acceptWriteLocked(FailableConsumer)},
    332     * {@link #applyReadLocked(FailableFunction)}, and {@link #applyWriteLocked(FailableFunction)}. By doing so, the
    333     * necessary protections are guaranteed.
    334     *
    335     * @param <O> The locked (hidden) objects type.
    336     */
    337    public static class StampedLockVisitor<O> extends LockVisitor<O, StampedLock> {
    338
    339        /**
    340         * Creates a new instance with the given locked object. This constructor is supposed to be used for subclassing
    341         * only. In general, it is suggested to use {@link LockingVisitors#stampedLockVisitor(Object)} instead.
    342         *
    343         * @param object The locked (hidden) object. The caller is supposed to drop all references to the locked object.
    344         * @param stampedLock the lock to use.
    345         */
    346        protected StampedLockVisitor(final O object, final StampedLock stampedLock) {
    347            super(object, stampedLock, stampedLock::asReadLock, stampedLock::asWriteLock);
    348        }
    349    }
    350
    351    /**
    352     * Creates a new instance of {@link ReadWriteLockVisitor} with the given (hidden) object and lock.
    353     *
    354     * @param <O> The locked objects type.
    355     * @param object The locked (hidden) object.
    356     * @param readWriteLock The lock to use.
    357     * @return The created instance, a {@link StampedLockVisitor lock} for the given object.
    358     * @since 3.13.0
    359     */
    360    public static <O> ReadWriteLockVisitor<O> create(final O object, final ReadWriteLock readWriteLock) {
    361        return new LockingVisitors.ReadWriteLockVisitor<>(object, readWriteLock);
    362    }
    363
    364    /**
    365     * Creates a new instance of {@link ReadWriteLockVisitor} with the given (hidden) object.
    366     *
    367     * @param <O> The locked objects type.
    368     * @param object The locked (hidden) object.
    369     * @return The created instance, a {@link StampedLockVisitor lock} for the given object.
    370     */
    371    public static <O> ReadWriteLockVisitor<O> reentrantReadWriteLockVisitor(final O object) {
    372        return create(object, new ReentrantReadWriteLock());
    373    }
    374
    375    /**
    376     * Creates a new instance of {@link StampedLockVisitor} with the given (hidden) object.
    377     *
    378     * @param <O> The locked objects type.
    379     * @param object The locked (hidden) object.
    380     * @return The created instance, a {@link StampedLockVisitor lock} for the given object.
    381     */
    382    public static <O> StampedLockVisitor<O> stampedLockVisitor(final O object) {
    383        return new LockingVisitors.StampedLockVisitor<>(object, new StampedLock());
    384    }
    385
    386}