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, final L lock, final Supplier<Lock> readLockSupplier, final Supplier<Lock> writeLockSupplier) {
122            this.object = Objects.requireNonNull(object, "object");
123            this.lock = Objects.requireNonNull(lock, "lock");
124            this.readLockSupplier = Objects.requireNonNull(readLockSupplier, "readLockSupplier");
125            this.writeLockSupplier = Objects.requireNonNull(writeLockSupplier, "writeLockSupplier");
126        }
127
128        /**
129         * <p>
130         * Provides read (shared, non-exclusive) access to the locked (hidden) object. More precisely, what the method
131         * will do (in the given order):
132         * </p>
133         * <ol>
134         * <li>Obtain a read (shared) lock on the locked (hidden) object. The current thread may block, until such a
135         * lock is granted.</li>
136         * <li>Invokes the given {@link FailableConsumer consumer}, passing the locked object as the parameter.</li>
137         * <li>Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the
138         * lock will be released anyways.</li>
139         * </ol>
140         *
141         * @param consumer The consumer, which is being invoked to use the hidden object, which will be passed as the
142         *        consumers parameter.
143         * @see #acceptWriteLocked(FailableConsumer)
144         * @see #applyReadLocked(FailableFunction)
145         */
146        public void acceptReadLocked(final FailableConsumer<O, ?> consumer) {
147            lockAcceptUnlock(readLockSupplier, consumer);
148        }
149
150        /**
151         * <p>
152         * Provides write (exclusive) access to the locked (hidden) object. More precisely, what the method will do (in
153         * the given order):
154         * </p>
155         * <ol>
156         * <li>Obtain a write (shared) lock on the locked (hidden) object. The current thread may block, until such a
157         * lock is granted.</li>
158         * <li>Invokes the given {@link FailableConsumer consumer}, passing the locked object as the parameter.</li>
159         * <li>Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the
160         * lock will be released anyways.</li>
161         * </ol>
162         *
163         * @param consumer The consumer, which is being invoked to use the hidden object, which will be passed as the
164         *        consumers parameter.
165         * @see #acceptReadLocked(FailableConsumer)
166         * @see #applyWriteLocked(FailableFunction)
167         */
168        public void acceptWriteLocked(final FailableConsumer<O, ?> consumer) {
169            lockAcceptUnlock(writeLockSupplier, consumer);
170        }
171
172        /**
173         * <p>
174         * Provides read (shared, non-exclusive) access to the locked (hidden) object for the purpose of computing a
175         * result object. More precisely, what the method will do (in the given order):
176         * </p>
177         * <ol>
178         * <li>Obtain a read (shared) lock on the locked (hidden) object. The current thread may block, until such a
179         * lock is granted.</li>
180         * <li>Invokes the given {@link FailableFunction function}, passing the locked object as the parameter,
181         * receiving the functions result.</li>
182         * <li>Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the
183         * lock will be released anyways.</li>
184         * <li>Return the result object, that has been received from the functions invocation.</li>
185         * </ol>
186         * <p>
187         * <em>Example:</em> Consider that the hidden object is a list, and we wish to know the current size of the
188         * list. This might be achieved with the following:
189         * </p>
190         * <pre>
191         * private Lock&lt;List&lt;Object&gt;&gt; listLock;
192         *
193         * public int getCurrentListSize() {
194         *     final Integer sizeInteger = listLock.applyReadLocked((list) -&gt; Integer.valueOf(list.size));
195         *     return sizeInteger.intValue();
196         * }
197         * </pre>
198         *
199         * @param <T> The result type (both the functions, and this method's.)
200         * @param function The function, which is being invoked to compute the result. The function will receive the
201         *        hidden object.
202         * @return The result object, which has been returned by the functions invocation.
203         * @throws IllegalStateException The result object would be, in fact, the hidden object. This would extend
204         *         access to the hidden object beyond this methods lifetime and will therefore be prevented.
205         * @see #acceptReadLocked(FailableConsumer)
206         * @see #applyWriteLocked(FailableFunction)
207         */
208        public <T> T applyReadLocked(final FailableFunction<O, T, ?> function) {
209            return lockApplyUnlock(readLockSupplier, function);
210        }
211
212        /**
213         * <p>
214         * Provides write (exclusive) access to the locked (hidden) object for the purpose of computing a result object.
215         * More precisely, what the method will do (in the given order):
216         * </p>
217         * <ol>
218         * <li>Obtain a read (shared) lock on the locked (hidden) object. The current thread may block, until such a
219         * lock is granted.</li>
220         * <li>Invokes the given {@link FailableFunction function}, passing the locked object as the parameter,
221         * receiving the functions result.</li>
222         * <li>Release the lock, as soon as the consumers invocation is done. If the invocation results in an error, the
223         * lock will be released anyways.</li>
224         * <li>Return the result object, that has been received from the functions invocation.</li>
225         * </ol>
226         *
227         * @param <T> The result type (both the functions, and this method's.)
228         * @param function The function, which is being invoked to compute the result. The function will receive the
229         *        hidden object.
230         * @return The result object, which has been returned by the functions invocation.
231         * @throws IllegalStateException The result object would be, in fact, the hidden object. This would extend
232         *         access to the hidden object beyond this methods lifetime and will therefore be prevented.
233         * @see #acceptReadLocked(FailableConsumer)
234         * @see #applyWriteLocked(FailableFunction)
235         */
236        public <T> T applyWriteLocked(final FailableFunction<O, T, ?> function) {
237            return lockApplyUnlock(writeLockSupplier, function);
238        }
239
240        /**
241         * Gets the lock.
242         *
243         * @return the lock.
244         */
245        public L getLock() {
246            return lock;
247        }
248
249        /**
250         * Gets the guarded object.
251         *
252         * @return the object.
253         */
254        public O getObject() {
255            return object;
256        }
257
258        /**
259         * This method provides the default implementation for {@link #acceptReadLocked(FailableConsumer)}, and
260         * {@link #acceptWriteLocked(FailableConsumer)}.
261         *
262         * @param lockSupplier A supplier for the lock. (This provides, in fact, a long, because a {@link StampedLock} is used
263         *        internally.)
264         * @param consumer The consumer, which is to be given access to the locked (hidden) object, which will be passed
265         *        as a parameter.
266         * @see #acceptReadLocked(FailableConsumer)
267         * @see #acceptWriteLocked(FailableConsumer)
268         */
269        protected void lockAcceptUnlock(final Supplier<Lock> lockSupplier, final FailableConsumer<O, ?> consumer) {
270            final Lock lock = lockSupplier.get();
271            lock.lock();
272            try {
273                consumer.accept(object);
274            } catch (final Throwable t) {
275                throw Failable.rethrow(t);
276            } finally {
277                lock.unlock();
278            }
279        }
280
281        /**
282         * This method provides the actual implementation for {@link #applyReadLocked(FailableFunction)}, and
283         * {@link #applyWriteLocked(FailableFunction)}.
284         *
285         * @param <T> The result type (both the functions, and this method's.)
286         * @param lockSupplier A supplier for the lock. (This provides, in fact, a long, because a {@link StampedLock} is used
287         *        internally.)
288         * @param function The function, which is being invoked to compute the result object. This function will receive
289         *        the locked (hidden) object as a parameter.
290         * @return The result object, which has been returned by the functions invocation.
291         * @throws IllegalStateException The result object would be, in fact, the hidden object. This would extend
292         *         access to the hidden object beyond this methods lifetime and will therefore be prevented.
293         * @see #applyReadLocked(FailableFunction)
294         * @see #applyWriteLocked(FailableFunction)
295         */
296        protected <T> T lockApplyUnlock(final Supplier<Lock> lockSupplier, final FailableFunction<O, T, ?> function) {
297            final Lock lock = lockSupplier.get();
298            lock.lock();
299            try {
300                return function.apply(object);
301            } catch (final Throwable t) {
302                throw Failable.rethrow(t);
303            } finally {
304                lock.unlock();
305            }
306        }
307
308    }
309
310    /**
311     * This class implements a wrapper for a locked (hidden) object, and provides the means to access it. The basic
312     * idea, is that the user code forsakes all references to the locked object, using only the wrapper object, and the
313     * accessor methods {@link #acceptReadLocked(FailableConsumer)}, {@link #acceptWriteLocked(FailableConsumer)},
314     * {@link #applyReadLocked(FailableFunction)}, and {@link #applyWriteLocked(FailableFunction)}. By doing so, the
315     * necessary protections are guaranteed.
316     *
317     * @param <O> The locked (hidden) objects type.
318     */
319    public static class ReadWriteLockVisitor<O> extends LockVisitor<O, ReadWriteLock> {
320
321        /**
322         * Creates a new instance with the given locked object. This constructor is supposed to be used for subclassing
323         * only. In general, it is suggested to use {@link LockingVisitors#stampedLockVisitor(Object)} instead.
324         *
325         * @param object The locked (hidden) object. The caller is supposed to drop all references to the locked object.
326         * @param readWriteLock the lock to use.
327         */
328        protected ReadWriteLockVisitor(final O object, final ReadWriteLock readWriteLock) {
329            super(object, readWriteLock, readWriteLock::readLock, readWriteLock::writeLock);
330        }
331    }
332
333    /**
334     * This class implements a wrapper for a locked (hidden) object, and provides the means to access it. The basic
335     * idea is that the user code forsakes all references to the locked object, using only the wrapper object, and the
336     * accessor methods {@link #acceptReadLocked(FailableConsumer)}, {@link #acceptWriteLocked(FailableConsumer)},
337     * {@link #applyReadLocked(FailableFunction)}, and {@link #applyWriteLocked(FailableFunction)}. By doing so, the
338     * necessary protections are guaranteed.
339     *
340     * @param <O> The locked (hidden) objects type.
341     */
342    public static class StampedLockVisitor<O> extends LockVisitor<O, StampedLock> {
343
344        /**
345         * Creates a new instance with the given locked object. This constructor is supposed to be used for subclassing
346         * only. In general, it is suggested to use {@link LockingVisitors#stampedLockVisitor(Object)} instead.
347         *
348         * @param object The locked (hidden) object. The caller is supposed to drop all references to the locked object.
349         * @param stampedLock the lock to use.
350         */
351        protected StampedLockVisitor(final O object, final StampedLock stampedLock) {
352            super(object, stampedLock, stampedLock::asReadLock, stampedLock::asWriteLock);
353        }
354    }
355
356    /**
357     * Creates a new instance of {@link ReadWriteLockVisitor} with the given (hidden) object.
358     *
359     * @param <O> The locked objects type.
360     * @param object The locked (hidden) object.
361     * @return The created instance, a {@link StampedLockVisitor lock} for the given object.
362     */
363    public static <O> ReadWriteLockVisitor<O> reentrantReadWriteLockVisitor(final O object) {
364        return new LockingVisitors.ReadWriteLockVisitor<>(object, new ReentrantReadWriteLock());
365    }
366
367    /**
368     * Creates a new instance of {@link StampedLockVisitor} with the given (hidden) object.
369     *
370     * @param <O> The locked objects type.
371     * @param object The locked (hidden) object.
372     * @return The created instance, a {@link StampedLockVisitor lock} for the given object.
373     */
374    public static <O> StampedLockVisitor<O> stampedLockVisitor(final O object) {
375        return new LockingVisitors.StampedLockVisitor<>(object, new StampedLock());
376    }
377
378}