/* * Copyright (c) 2016-present, Facebook, Inc. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ package codetoanalyze.java.checkers; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import javax.annotation.concurrent.ThreadSafe; @ThreadSafe public class Locks { Integer f; Lock mLock; ReadWriteLock mReadWriteLock; ReentrantLock mReentrantLock; ReentrantReadWriteLock mReentrantReadWriteLock; // we allow this for now public void FN_lockInOneBranchBad(boolean b) { if (b) { mLock.lock(); } f = 24; if (b) { mLock.unlock(); } } public void afterUnlockBad() { mLock.lock(); mLock.unlock(); f = 42; } public void afterReentrantLockUnlockBad() { mReentrantLock.lock(); mReentrantLock.unlock(); f = 42; } public void afterWriteLockUnlockBad() { mReentrantReadWriteLock.writeLock().lock(); mReentrantReadWriteLock.writeLock().unlock(); f = 42; } public void lockOk() { mLock.lock(); f = 42; mLock.unlock(); } public void lockBothBranchesOk(boolean b) { if (b) { mLock.lock(); } else { mLock.lock(); } f = 42; mLock.unlock(); } public void reentrantLockOk() { mReentrantLock.lock(); f = 42; mReentrantLock.unlock(); } public void normalLockTryLockOk() { if (mLock.tryLock()) { f = 42; mLock.unlock(); } } public void reentrantLockTryLockOk() { if (mReentrantLock.tryLock()) { f = 42; mReentrantLock.unlock(); } } public void tryLockNoCheckBad() { mReentrantLock.tryLock(); // might return false f = 42; } public void tryLockWrongBranchBad() { if (mReentrantLock.tryLock()) { } else { f = 42; } } public void tryLockPropagateOk() { boolean result = mReentrantLock.tryLock(); boolean copy = result; if (copy) { f = 42; } } public void negatedReentrantLockTryLockBad() { if (!mReentrantLock.tryLock()) { f = 42; } } public void negatedReentrantLockTryLockOk() { if (!mReentrantLock.tryLock()) { } else { f = 42; } } // we could catch this by invalidating the choice predicates whenever we update the lock domain public void FN_tryLockStaleBad() { boolean result = mReentrantLock.tryLock(); mReentrantLock.unlock(); if (result) { f = 42; // oops, actually not safe } } public void reentrantLockInterruptiblyOk() throws InterruptedException { mReentrantLock.lockInterruptibly(); f = 42; mReentrantLock.unlock(); } private void acquireLock() { mLock.lock(); } public void acquireLockInCalleeOk() { acquireLock(); f = 42; mLock.unlock(); } public void writeLockOk() { mReadWriteLock.writeLock().lock(); f = 42; mReadWriteLock.writeLock().unlock(); } public void reentrantWriteLockOk() { mReentrantReadWriteLock.writeLock().lock(); f = 42; mReentrantReadWriteLock.writeLock().unlock(); } private void releaseLock() { mLock.unlock(); } void nested1Ok() { synchronized (this) { synchronized (this) { } // a bad abstraction of locks will treat this as unlocked... f = 32; } } void nested2Ok() { synchronized (this) { synchronized (this) { f = 32; } } } void nested3Ok() { synchronized (this) { f = 32; synchronized (this) { } } } void nested1Bad() { synchronized (this) { synchronized (this) { } } f = 32; } void nested2Bad() { synchronized (this) { } f = 32; synchronized (this) { } } void nested3Bad() { synchronized (this) { } synchronized (this) { } f = 32; } void useLock() { synchronized (this) { } } void useLockInCalleeBad() { useLock(); f = 32; } void lockInLoopOk(int i) { while (i > 0) { i++; mLock.lock(); } f = 32; } void unlockInLoopOk(int i) { mLock.lock(); while (i > 0) { i++; mLock.unlock(); } f = 32; } void lockInLoopLexicalBad(int i) { while (i > 0) { i++; synchronized (this) { } } f = 32; } void lockInLoopLexicalOk(int i) { while (i > 0) { i++; synchronized (this) { f = 32; } } } void loopInLockLexicalBad(int i) { synchronized (this) { while (i > 0) { i++; } f = 32; } } public void unlockOneLockOk() { mLock.lock(); mReentrantLock.lock(); mReentrantLock.unlock(); f = 42; mLock.unlock(); } // ... or here public void FN_releaseLockInCalleeBad() { mLock.lock(); releaseLock(); f = 42; } // we shouldn't be able to write when holding a readLock public void FN_readLockOk() { mReentrantReadWriteLock.readLock().lock(); f = 42; mReentrantReadWriteLock.readLock().unlock(); } boolean mField; boolean readUnderLockOk() { synchronized (this) { return mField; } } void writeUnderLockOk() { synchronized (this) { mField = true; } } boolean readOutsideLock1Bad() { synchronized (this) { } return mField; } boolean readOutsideLock2Bad() { boolean tmp = mField; synchronized (this) { } return tmp; } public boolean readInTryCatchWithLockOk() { mLock.lock(); try { return mField; } finally { mLock.unlock(); } } public void writeInsideTryCatchWithLockOk() { mLock.lock(); try { mField = true; } finally { mLock.unlock(); } } Object mField2; private synchronized void lockedWriteInCallee() { this.mField2 = null; } public static void ownedLockedReadOk() { Locks owned = new Locks(); owned.lockedWriteInCallee(); } public Object unownedReadOk() { // safe because the only other access to mField is owned return this.mField2; } Object mField3; private synchronized void lockedWriteInCallee2() { this.mField3 = null; } public void unownedLockedWriteOk() { lockedWriteInCallee2(); } public Object unownedReadBad() { return this.mField3; } }