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