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