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