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., & 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}