mirror of https://github.com/M66B/FairEmail.git
113 lines
3.6 KiB
Java
113 lines
3.6 KiB
Java
/*
|
|
* Copyright 2019 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package androidx.room.util;
|
|
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.RestrictTo;
|
|
|
|
import java.io.File;
|
|
import java.io.FileOutputStream;
|
|
import java.io.IOException;
|
|
import java.nio.channels.FileChannel;
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
import java.util.concurrent.locks.Lock;
|
|
import java.util.concurrent.locks.ReentrantLock;
|
|
|
|
/**
|
|
* Utility class for in-process and multi-process key-based lock mechanism for safely copying
|
|
* database files.
|
|
* <p>
|
|
* Acquiring the lock will be quick if no other thread or process has a lock with the same key.
|
|
* But if the lock is already held then acquiring it will block, until the other thread or process
|
|
* releases the lock. Note that the key and lock directory must be the same to achieve
|
|
* synchronization.
|
|
* <p>
|
|
* Locking is done via two levels:
|
|
* <ol>
|
|
* <li>
|
|
* Thread locking within the same JVM process is done via a map of String key to ReentrantLock
|
|
* objects.
|
|
* <li>
|
|
* Multi-process locking is done via a lock file whose name contains the key and FileLock
|
|
* objects.
|
|
*
|
|
* @hide
|
|
*/
|
|
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
|
|
public class CopyLock {
|
|
|
|
// in-process lock map
|
|
private static final Map<String, Lock> sThreadLocks = new HashMap<>();
|
|
|
|
private final File mCopyLockFile;
|
|
private final Lock mThreadLock;
|
|
private final boolean mFileLevelLock;
|
|
private FileChannel mLockChannel;
|
|
|
|
/**
|
|
* Creates a lock with {@code name} and using {@code lockDir} as the directory for the
|
|
* lock files.
|
|
* @param name the name of this lock.
|
|
* @param lockDir the directory where the lock files will be located.
|
|
* @param processLock whether to use file for process level locking or not.
|
|
*/
|
|
public CopyLock(@NonNull String name, @NonNull File lockDir, boolean processLock) {
|
|
mCopyLockFile = new File(lockDir, name + ".lck");
|
|
mThreadLock = getThreadLock(mCopyLockFile.getAbsolutePath());
|
|
mFileLevelLock = processLock;
|
|
}
|
|
|
|
/**
|
|
* Attempts to grab the lock, blocking if already held by another thread or process.
|
|
*/
|
|
public void lock() {
|
|
mThreadLock.lock();
|
|
if (mFileLevelLock) {
|
|
try {
|
|
mLockChannel = new FileOutputStream(mCopyLockFile).getChannel();
|
|
mLockChannel.lock();
|
|
} catch (IOException e) {
|
|
throw new IllegalStateException("Unable to grab copy lock.", e);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Releases the lock.
|
|
*/
|
|
public void unlock() {
|
|
if (mLockChannel != null) {
|
|
try {
|
|
mLockChannel.close();
|
|
} catch (IOException ignored) { }
|
|
}
|
|
mThreadLock.unlock();
|
|
}
|
|
|
|
private static Lock getThreadLock(String key) {
|
|
synchronized (sThreadLocks) {
|
|
Lock threadLock = sThreadLocks.get(key);
|
|
if (threadLock == null) {
|
|
threadLock = new ReentrantLock();
|
|
sThreadLocks.put(key, threadLock);
|
|
}
|
|
return threadLock;
|
|
}
|
|
}
|
|
}
|