149b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin/* 249b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * Copyright (C) 2014 The Android Open Source Project 349b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * 449b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * Licensed under the Apache License, Version 2.0 (the "License"); 549b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * you may not use this file except in compliance with the License. 649b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * You may obtain a copy of the License at 749b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * 849b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * http://www.apache.org/licenses/LICENSE-2.0 949b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * 1049b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * Unless required by applicable law or agreed to in writing, software 1149b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * distributed under the License is distributed on an "AS IS" BASIS, 1249b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1349b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * See the License for the specific language governing permissions and 1449b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * limitations under the License. 1549b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin */ 1649b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 1749b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkinpackage android.hardware.camera2.utils; 1849b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 1949b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkinimport android.util.Log; 2049b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 2149b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkinimport java.util.concurrent.locks.Condition; 2249b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkinimport java.util.concurrent.locks.ReentrantLock; 2349b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 2449b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin/** 2549b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * Implement a shared/exclusive lock that can be closed. 2649b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * 2749b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * <p>A shared lock can be acquired if any other shared locks are also acquired. An 2849b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * exclusive lock acquire will block until all shared locks have been released.</p> 2949b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * 3049b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * <p>Locks are re-entrant; trying to acquire another lock (of the same type) 3149b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * while a lock is already held will immediately succeed.</p> 3249b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * 3349b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * <p>Acquiring to acquire a shared lock while holding an exclusive lock or vice versa is not 3449b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * supported; attempting it will throw an {@link IllegalStateException}.</p> 3549b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * 3649b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * <p>If the lock is closed, all future and current acquires will immediately return {@code null}. 3749b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * </p> 3849b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin */ 3949b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkinpublic class CloseableLock implements AutoCloseable { 4049b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 4149b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin private static final boolean VERBOSE = false; 4249b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 4349b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin private final String TAG = "CloseableLock"; 4449b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin private final String mName; 4549b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 4649b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin private volatile boolean mClosed = false; 4749b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 4849b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin /** If an exclusive lock is acquired by some thread. */ 4949b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin private boolean mExclusive = false; 5049b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin /** 5149b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * How many shared locks are acquired by any thread: 5249b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * 5349b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * <p>Reentrant locking increments this. If an exclusive lock is held, 5449b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * this value will stay at 0.</p> 5549b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin */ 5649b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin private int mSharedLocks = 0; 5749b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 5849b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin private final ReentrantLock mLock = new ReentrantLock(); 5949b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin /** This condition automatically releases mLock when waiting; re-acquiring it after notify */ 6049b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin private final Condition mCondition = mLock.newCondition(); 6149b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 6249b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin /** How many times the current thread is holding the lock */ 6349b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin private final ThreadLocal<Integer> mLockCount = 6449b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin new ThreadLocal<Integer>() { 6549b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin @Override protected Integer initialValue() { 6649b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin return 0; 6749b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin } 6849b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin }; 6949b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 7049b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin /** 7149b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * Helper class to release a lock at the end of a try-with-resources statement. 7249b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin */ 7349b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin public class ScopedLock implements AutoCloseable { 7449b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin private ScopedLock() {} 7549b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 7649b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin /** Release the lock with {@link CloseableLock#releaseLock}. */ 7749b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin @Override 7849b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin public void close() { 7949b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin releaseLock(); 8049b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin } 8149b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin } 8249b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 8349b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin /** 8449b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * Create a new instance; starts out with 0 locks acquired. 8549b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin */ 8649b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin public CloseableLock() { 8749b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin mName = ""; 8849b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin } 8949b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 9049b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin /** 9149b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * Create a new instance; starts out with 0 locks acquired. 9249b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * 9349b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * @param name set an optional name for logging functionality 9449b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin */ 9549b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin public CloseableLock(String name) { 9649b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin mName = name; 9749b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin } 9849b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 9949b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin /** 10049b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * Acquires the lock exclusively (blocking), marks it as closed, then releases the lock. 10149b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * 10249b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * <p>Marking a lock as closed will fail all further acquisition attempts; 10349b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * it will also immediately unblock all other threads currently trying to acquire a lock.</p> 10449b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * 10549b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * <p>This operation is idempotent; calling it more than once has no effect.</p> 10649b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * 10749b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * @throws IllegalStateException 10849b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * if an attempt is made to {@code close} while this thread has a lock acquired 10949b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin */ 11049b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin @Override 11149b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin public void close() { 11249b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin if (mClosed) { 1132cdfa261f44182292d07b7b3b1e9fce8376e4df5Ruben Brunk if (VERBOSE) { 1142cdfa261f44182292d07b7b3b1e9fce8376e4df5Ruben Brunk log("close - already closed; ignoring"); 1152cdfa261f44182292d07b7b3b1e9fce8376e4df5Ruben Brunk } 11649b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin return; 11749b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin } 11849b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 11949b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin ScopedLock scoper = acquireExclusiveLock(); 12049b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin // Already closed by another thread? 12149b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin if (scoper == null) { 12249b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin return; 12349b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin } else if (mLockCount.get() != 1) { 12449b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin // Future: may want to add a #releaseAndClose to allow this. 12549b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin throw new IllegalStateException( 12649b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin "Cannot close while one or more acquired locks are being held by this " + 12749b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin "thread; release all other locks first"); 12849b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin } 12949b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 13049b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin try { 13149b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin mLock.lock(); 13249b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 13349b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin mClosed = true; 13449b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin mExclusive = false; 13549b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin mSharedLocks = 0; 13649b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin mLockCount.remove(); 13749b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 13849b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin // Notify all threads that are waiting to unblock and return immediately 13949b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin mCondition.signalAll(); 14049b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin } finally { 14149b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin mLock.unlock(); 14249b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin } 14349b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 1442cdfa261f44182292d07b7b3b1e9fce8376e4df5Ruben Brunk if (VERBOSE) { 1452cdfa261f44182292d07b7b3b1e9fce8376e4df5Ruben Brunk log("close - completed"); 1462cdfa261f44182292d07b7b3b1e9fce8376e4df5Ruben Brunk } 14749b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin } 14849b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 14949b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin /** 15049b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * Try to acquire the lock non-exclusively, blocking until the operation completes. 15149b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * 15249b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * <p>If the lock has already been closed, or being closed before this operation returns, 15349b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * the call will immediately return {@code false}.</p> 15449b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * 15549b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * <p>If other threads hold a non-exclusive lock (and the lock is not yet closed), 15649b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * this operation will return immediately. If another thread holds an exclusive lock, 15749b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * this thread will block until the exclusive lock has been released.</p> 15849b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * 15949b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * <p>This lock is re-entrant; acquiring more than one non-exclusive lock per thread is 16049b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * supported, and must be matched by an equal number of {@link #releaseLock} calls.</p> 16149b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * 16249b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * @return {@code ScopedLock} instance if the lock was acquired, or {@code null} if the lock 16349b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * was already closed. 16449b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * 16549b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * @throws IllegalStateException if this thread is already holding an exclusive lock 16649b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin */ 16749b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin public ScopedLock acquireLock() { 16849b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 16949b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin int ownedLocks; 17049b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 17149b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin try { 17249b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin mLock.lock(); 17349b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 17449b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin // Lock is already closed, all further acquisitions will fail 17549b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin if (mClosed) { 1762cdfa261f44182292d07b7b3b1e9fce8376e4df5Ruben Brunk if (VERBOSE) { 1772cdfa261f44182292d07b7b3b1e9fce8376e4df5Ruben Brunk log("acquire lock early aborted (already closed)"); 1782cdfa261f44182292d07b7b3b1e9fce8376e4df5Ruben Brunk } 17949b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin return null; 18049b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin } 18149b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 18249b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin ownedLocks = mLockCount.get(); 18349b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 18449b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin // This thread is already holding an exclusive lock 18549b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin if (mExclusive && ownedLocks > 0) { 18649b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin throw new IllegalStateException( 18749b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin "Cannot acquire shared lock while holding exclusive lock"); 18849b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin } 18949b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 19049b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin // Is another thread holding the exclusive lock? Block until we can get in. 19149b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin while (mExclusive) { 19249b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin mCondition.awaitUninterruptibly(); 19349b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 19449b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin // Did another thread #close while we were waiting? Unblock immediately. 19549b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin if (mClosed) { 1962cdfa261f44182292d07b7b3b1e9fce8376e4df5Ruben Brunk if (VERBOSE) { 1972cdfa261f44182292d07b7b3b1e9fce8376e4df5Ruben Brunk log("acquire lock unblocked aborted (already closed)"); 1982cdfa261f44182292d07b7b3b1e9fce8376e4df5Ruben Brunk } 19949b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin return null; 20049b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin } 20149b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin } 20249b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 20349b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin mSharedLocks++; 20449b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 20549b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin ownedLocks = mLockCount.get() + 1; 20649b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin mLockCount.set(ownedLocks); 20749b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin } finally { 20849b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin mLock.unlock(); 20949b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin } 21049b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 2112cdfa261f44182292d07b7b3b1e9fce8376e4df5Ruben Brunk if (VERBOSE) { 2122cdfa261f44182292d07b7b3b1e9fce8376e4df5Ruben Brunk log("acquired lock (local own count = " + ownedLocks + ")"); 2132cdfa261f44182292d07b7b3b1e9fce8376e4df5Ruben Brunk } 21449b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin return new ScopedLock(); 21549b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin } 21649b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 21749b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin /** 21849b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * Try to acquire the lock exclusively, blocking until all other threads release their locks. 21949b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * 22049b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * <p>If the lock has already been closed, or being closed before this operation returns, 22149b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * the call will immediately return {@code false}.</p> 22249b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * 22349b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * <p>If any other threads are holding a lock, this thread will block until all 22449b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * other locks are released.</p> 22549b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * 22649b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * <p>This lock is re-entrant; acquiring more than one exclusive lock per thread is supported, 22749b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * and must be matched by an equal number of {@link #releaseLock} calls.</p> 22849b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * 22949b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * @return {@code ScopedLock} instance if the lock was acquired, or {@code null} if the lock 23049b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * was already closed. 23149b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * 23249b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * @throws IllegalStateException 23349b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * if an attempt is made to acquire an exclusive lock while already holding a lock 23449b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin */ 23549b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin public ScopedLock acquireExclusiveLock() { 23649b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 23749b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin int ownedLocks; 23849b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 23949b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin try { 24049b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin mLock.lock(); 24149b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 24249b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin // Lock is already closed, all further acquisitions will fail 24349b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin if (mClosed) { 2442cdfa261f44182292d07b7b3b1e9fce8376e4df5Ruben Brunk if (VERBOSE) { 2452cdfa261f44182292d07b7b3b1e9fce8376e4df5Ruben Brunk log("acquire exclusive lock early aborted (already closed)"); 2462cdfa261f44182292d07b7b3b1e9fce8376e4df5Ruben Brunk } 24749b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin return null; 24849b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin } 24949b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 25049b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin ownedLocks = mLockCount.get(); 25149b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 25249b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin // This thread is already holding a shared lock 25349b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin if (!mExclusive && ownedLocks > 0) { 25449b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin throw new IllegalStateException( 25549b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin "Cannot acquire exclusive lock while holding shared lock"); 25649b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin } 25749b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 25849b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin /* 25949b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * Is another thread holding the lock? Block until we can get in. 26049b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * 26149b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * If we are already holding the lock, always let it through since 26249b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * we are just reentering the exclusive lock. 26349b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin */ 26449b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin while (ownedLocks == 0 && (mExclusive || mSharedLocks > 0)) { 26549b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin mCondition.awaitUninterruptibly(); 26649b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 26749b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin // Did another thread #close while we were waiting? Unblock immediately. 26849b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin if (mClosed) { 2692cdfa261f44182292d07b7b3b1e9fce8376e4df5Ruben Brunk if (VERBOSE) { 2702cdfa261f44182292d07b7b3b1e9fce8376e4df5Ruben Brunk log("acquire exclusive lock unblocked aborted (already closed)"); 2712cdfa261f44182292d07b7b3b1e9fce8376e4df5Ruben Brunk } 27249b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin return null; 27349b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin } 27449b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin } 27549b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 27649b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin mExclusive = true; 27749b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 27849b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin ownedLocks = mLockCount.get() + 1; 27949b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin mLockCount.set(ownedLocks); 28049b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin } finally { 28149b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin mLock.unlock(); 28249b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin } 28349b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 2842cdfa261f44182292d07b7b3b1e9fce8376e4df5Ruben Brunk if (VERBOSE) { 2852cdfa261f44182292d07b7b3b1e9fce8376e4df5Ruben Brunk log("acquired exclusive lock (local own count = " + ownedLocks + ")"); 2862cdfa261f44182292d07b7b3b1e9fce8376e4df5Ruben Brunk } 28749b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin return new ScopedLock(); 28849b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin } 28949b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 29049b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin /** 29149b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * Release a single lock that was acquired. 29249b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * 29349b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * <p>Any other other that is blocked and trying to acquire a lock will get a chance 29449b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * to acquire the lock.</p> 29549b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * 29649b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin * @throws IllegalStateException if no locks were acquired, or if the lock was already closed 29749b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin */ 29849b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin public void releaseLock() { 29949b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin if (mLockCount.get() <= 0) { 30049b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin throw new IllegalStateException( 30149b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin "Cannot release lock that was not acquired by this thread"); 30249b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin } 30349b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 30449b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin int ownedLocks; 30549b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 30649b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin try { 30749b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin mLock.lock(); 30849b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 30949b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin // Lock is already closed, it couldn't have been acquired in the first place 31049b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin if (mClosed) { 31149b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin throw new IllegalStateException("Do not release after the lock has been closed"); 31249b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin } 31349b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 31449b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin if (!mExclusive) { 31549b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin mSharedLocks--; 31649b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin } else { 31749b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin if (mSharedLocks != 0) { 31849b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin throw new AssertionError("Too many shared locks " + mSharedLocks); 31949b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin } 32049b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin } 32149b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 32249b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin ownedLocks = mLockCount.get() - 1; 32349b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin mLockCount.set(ownedLocks); 32449b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 32549b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin if (ownedLocks == 0 && mExclusive) { 32649b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin // Wake up any threads that might be waiting for the exclusive lock to be released 32749b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin mExclusive = false; 32849b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin mCondition.signalAll(); 32949b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin } else if (ownedLocks == 0 && mSharedLocks == 0) { 33049b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin // Wake up any threads that might be trying to get the exclusive lock 33149b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin mCondition.signalAll(); 33249b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin } 33349b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin } finally { 33449b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin mLock.unlock(); 33549b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin } 33649b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 3372cdfa261f44182292d07b7b3b1e9fce8376e4df5Ruben Brunk if (VERBOSE) { 3382cdfa261f44182292d07b7b3b1e9fce8376e4df5Ruben Brunk log("released lock (local lock count " + ownedLocks + ")"); 3392cdfa261f44182292d07b7b3b1e9fce8376e4df5Ruben Brunk } 34049b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin } 34149b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 34249b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin private void log(String what) { 3432cdfa261f44182292d07b7b3b1e9fce8376e4df5Ruben Brunk Log.v(TAG + "[" + mName + "]", what); 34449b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin } 34549b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin 34649b2b135105e5ca5dc9547f4c6de473bebad647dIgor Murashkin} 347