Summary: Starvation is currently path insensitive. Two special cases of sensitivity cover a large range of useful cases: - sensitivity on whether the current thread is a UI/background thread; - sensitivity on whether a lock can be acquired (without blocking) or not. We add a few tests capturing some of the false positives and negatives of the current analysis. Reviewed By: mityal Differential Revision: D17907492 fbshipit-source-id: fbce896acmaster
parent
5835139860
commit
31bb9b399a
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Tests documenting FPs and FNs due to lack of sensitivity in starvation analysis
|
||||||
|
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
|
||||||
|
class LockSensitivity {
|
||||||
|
Lock lockA, lockB;
|
||||||
|
|
||||||
|
// In the following two methods, AB vs BA deadlock pattern
|
||||||
|
// remains undetected since one of the locks happens via `tryLock` and result check.
|
||||||
|
|
||||||
|
public void FN_tryLockDeadlockAB_Bad() {
|
||||||
|
boolean locked = lockA.tryLock();
|
||||||
|
if (locked) {
|
||||||
|
lockB.lock();
|
||||||
|
lockB.unlock();
|
||||||
|
lockA.unlock();
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void FN_tryLockDeadlockBA_Bad() {
|
||||||
|
lockB.lock();
|
||||||
|
lockA.lock(); // deadlock: `lockA` may be locked via `tryLock()` above
|
||||||
|
lockA.unlock();
|
||||||
|
lockB.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Asserting a lock is held is not the same as taking it wrt deadlocks.
|
||||||
|
// In the following two methods, AB vs BA pattern is wrongly detected.
|
||||||
|
|
||||||
|
Object monitorA, monitorB;
|
||||||
|
|
||||||
|
public void FP_assertHoldsLockAB_Ok() {
|
||||||
|
OurThreadUtils.assertHoldsLock(monitorA);
|
||||||
|
OurThreadUtils.assertHoldsLock(monitorB);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void FP_assertHoldsLockBA_Ok() {
|
||||||
|
OurThreadUtils.assertHoldsLock(monitorB);
|
||||||
|
OurThreadUtils.assertHoldsLock(monitorA);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Examples where the code checks what thread it's currently running on.
|
||||||
|
class ThreadSensitivity {
|
||||||
|
Object monitorA, monitorB;
|
||||||
|
|
||||||
|
void FN_conditionalAssertMainThread_Bad(boolean b) {
|
||||||
|
if (b) {
|
||||||
|
// this branch asserts on Main thread
|
||||||
|
OurThreadUtils.assertMainThread();
|
||||||
|
synchronized (monitorA) {
|
||||||
|
synchronized (monitorB) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// this branch asserts nothing, so may run in parallel with the other branch
|
||||||
|
synchronized (monitorB) {
|
||||||
|
synchronized (monitorA) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object monitorC, monitorD;
|
||||||
|
|
||||||
|
// the branches in the following two methods are both on main/UI thread so cannot deadlock
|
||||||
|
|
||||||
|
void FP_conditionalIsMainThread_Ok() {
|
||||||
|
if (OurThreadUtils.isMainThread()) {
|
||||||
|
synchronized (monitorC) {
|
||||||
|
synchronized (monitorD) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FP_conditionalIsUiThread_Ok() {
|
||||||
|
if (OurThreadUtils.isUiThread()) {
|
||||||
|
synchronized (monitorD) {
|
||||||
|
synchronized (monitorC) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object monitorE, monitorF;
|
||||||
|
// identical to the first case above but negated
|
||||||
|
void conditionalNegatedIsMainThread_Bad() {
|
||||||
|
if (!OurThreadUtils.isMainThread()) {
|
||||||
|
synchronized (monitorE) {
|
||||||
|
synchronized (monitorF) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
synchronized (monitorF) {
|
||||||
|
synchronized (monitorE) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object monitorG, monitorH;
|
||||||
|
|
||||||
|
public void FN_confusedAssertBad(boolean b, boolean c) {
|
||||||
|
if (b) {
|
||||||
|
OurThreadUtils.assertOnBackgroundThread();
|
||||||
|
} else {
|
||||||
|
OurThreadUtils.assertOnUiThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
// not sure if we're on UI or background, should report
|
||||||
|
if (c) {
|
||||||
|
synchronized (monitorG) {
|
||||||
|
synchronized (monitorH) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
synchronized (monitorH) {
|
||||||
|
synchronized (monitorG) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue