1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.providers.media.photopicker.sync;
18 
19 import android.util.Log;
20 
21 import androidx.annotation.NonNull;
22 
23 import com.android.providers.media.photopicker.util.exceptions.UnableToAcquireLockException;
24 
25 import java.util.concurrent.TimeUnit;
26 import java.util.concurrent.locks.ReentrantLock;
27 
28 /**
29  * A Reentrant lock that implements AutoCloseable interface.
30  */
31 public class CloseableReentrantLock extends ReentrantLock implements AutoCloseable {
32     private static final String TAG = CloseableReentrantLock.class.getSimpleName();
33     private final String mLockName;
34 
CloseableReentrantLock(@onNull String lockName)35     public CloseableReentrantLock(@NonNull String lockName) {
36         super();
37         mLockName = lockName;
38     }
39 
40     /**
41      * Try to acquire lock with a timeout after running some validations.
42      */
lockWithTimeout(long timeout, TimeUnit unit)43     public CloseableReentrantLock lockWithTimeout(long timeout, TimeUnit unit)
44             throws UnableToAcquireLockException {
45         try {
46             final boolean success =
47                     this.tryLock(timeout, unit);
48             if (!success) {
49                 throw new UnableToAcquireLockException(
50                         "Could not acquire the lock within timeout " + this);
51             }
52             Log.d(TAG, "Successfully acquired lock " + this);
53             return this;
54         } catch (InterruptedException e) {
55             throw new UnableToAcquireLockException(
56                     "Interrupted while waiting for lock " + this, e);
57         }
58     }
59 
60     @Override
close()61     public void close() {
62         unlock();
63     }
64 
65     /**
66      * Attempt to release the lock and swallow IllegalMonitorStateException, if thrown.
67      */
68     @Override
lock()69     public void lock() {
70         super.lock();
71         Log.d(TAG, "Successfully acquired lock " + this);
72     }
73 
74     /**
75      * Attempt to release the lock and swallow IllegalMonitorStateException, if thrown.
76      */
77     @Override
unlock()78     public void unlock() {
79         try {
80             super.unlock();
81             Log.d(TAG, "Successfully released lock " + this);
82         } catch (IllegalMonitorStateException e) {
83             Log.e(TAG, "Tried to release a lock that is not held by this thread - " + this);
84         }
85     }
86 
87     @Override
toString()88     public String toString() {
89         return super.toString() + ". Lock Name = " + mLockName
90                 + ". Threads that may be waiting to acquire this lock = " + getQueuedThreads();
91     }
92 }
93