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