1 /*
2  * This work is licensed under the Creative Commons Attribution-ShareAlike 4.0
3  * International License. To view a copy of this license, visit
4  * https://creativecommons.org/licenses/by-sa/4.0/ or send a letter to
5  * Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
6  *
7  * Copyright (c) 2017, https://stackoverflow.com/users/7583219/skoskav
8  * Copyright (c) 2011, https://stackoverflow.com/questions/6965731/are-locks-autocloseable
9  * Portions Copyright (c) 2019-2020, Chris Fraire <cfraire@me.com>.
10  *
11  * Used under CC 4 with modifications noted as follows as required by license:
12  * 2019-09-10 -- cfraire@me.com, derived to use for ReentrantReadWriteLock.
13  * 2020-04-21 -- cfraire@me.com, updated for proper handling re Serializable.
14  */
15 
16 package org.opengrok.indexer.util;
17 
18 import java.util.concurrent.locks.ReentrantReadWriteLock;
19 
20 /**
21  * Represents a subclass of {@link ReentrantReadWriteLock} that can return
22  * {@link ResourceLock} instances.
23  */
24 public final class CloseableReentrantReadWriteLock extends ReentrantReadWriteLock {
25 
26     private static final long serialVersionUID = 95L;
27 
28     private transient ResourceLock readUnlocker = newReadUnlocker();
29 
30     private transient ResourceLock writeUnlocker = newWriteUnlocker();
31 
32     /**
33      * @return a defined {@link ResourceLock} once the {@link #readLock()} has
34      * been acquired
35      */
readLockAsResource()36     public ResourceLock readLockAsResource() {
37         /*
38          * A subclass of ReentrantReadWriteLock is forced to be serializable, so
39          * we would have to handle where serialization can short-circuit field
40          * initialization above and leave the instance's fields null.
41          * Consequently, the fields cannot be final. They are encapsulating
42          * fixed logic, so we can optimize and choose not to even bother
43          * serializing by declaring transient and handling below.
44          */
45         ResourceLock unlocker = readUnlocker;
46         if (unlocker == null) {
47             unlocker = newReadUnlocker();
48             // No synchronization necessary since overwrite is of no matter.
49             readUnlocker = unlocker;
50         }
51         readLock().lock();
52         return unlocker;
53     }
54 
55     /**
56      * @return a defined {@link ResourceLock} once the {@link #writeLock()}
57      * has been acquired
58      */
writeLockAsResource()59     public ResourceLock writeLockAsResource() {
60         /*
61          * A subclass of ReentrantReadWriteLock is forced to be serializable, so
62          * we would have to handle where serialization can short-circuit field
63          * initialization above and leave the instance's fields null.
64          * Consequently, the fields cannot be final. They are encapsulating
65          * fixed logic, so we can optimize and choose not to even bother
66          * serializing by declaring transient and handling below.
67          */
68         ResourceLock unlocker = writeUnlocker;
69         if (unlocker == null) {
70             unlocker = newWriteUnlocker();
71             // No synchronization necessary since overwrite is of no matter.
72             writeUnlocker = unlocker;
73         }
74         writeLock().lock();
75         return unlocker;
76     }
77 
newReadUnlocker()78     private ResourceLock newReadUnlocker() {
79         return () -> this.readLock().unlock();
80     }
81 
newWriteUnlocker()82     private ResourceLock newWriteUnlocker() {
83         return () -> this.writeLock().unlock();
84     }
85 }
86