You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

189 lines
4.1 KiB

/*
* 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.
*/
package codetoanalyze.java.checkers;
import com.facebook.infer.annotation.ThreadSafe;
import com.google.common.annotations.VisibleForTesting;
import javax.annotation.concurrent.NotThreadSafe;
@ThreadSafe
public class ThreadSafeExample {
/*Included to make sure infer does not report on class initializers*/
static Class<?> A = ThreadSafeExample.class;
Integer f;
public ThreadSafeExample() {
f = 86;
}
public void tsOK() {
synchronized (this) {
f = 42;
}
}
public void tsBad() {
f = 24;
}
public void recursiveBad() {
f = 44;
recursiveBad();
}
private void evenOk() {
f = 44;
oddBad();
}
public void oddBad() {
evenOk(); // should report here
}
// shouldn't report here because it's a private method
private void assignInPrivateMethodOk() {
f = 24;
}
// but should report here, because now it's called
public void callPublicMethodBad() {
assignInPrivateMethodOk();
}
private void callAssignInPrivateMethod() {
assignInPrivateMethodOk();
}
// should report a deeperTraceBade -> callAssignInPrivateMethod -> assignInPrivateMethodOk trace
public void deeperTraceBad() {
callAssignInPrivateMethod();
}
public synchronized void callFromSynchronizedPublicMethodOk() {
assignInPrivateMethodOk();
}
private synchronized void synchronizedCallerOk() {
assignInPrivateMethodOk();
}
public void callFromUnsynchronizedPublicMethodOk() {
synchronizedCallerOk();
}
// although the constructor touches f, we shouldn't complain here
public void callConstructorOk() {
new ThreadSafeExample();
}
private Object returnConstructorOk() {
return new ThreadSafeExample();
}
public void transitivelyCallConstructorOk() {
returnConstructorOk();
}
volatile Object volatileField;
// we don't warn on unsafe writes to volatile fields
public void unsafeVolatileWriteOk() {
this.volatileField = new Object();
}
// don't count the method as public if it's marked VisibleForTesting
@VisibleForTesting
public void visibleForTestingNotPublicOk() {
this.f = 47;
}
// but do complain if a VisibleForTesting method is called from a public method
public void callVisibleForTestingBad() {
visibleForTestingNotPublicOk();
}
Object sharedField;
private void writePrivateSharedFieldOk() {
this.sharedField = new Object();
}
public Object returnSharedFieldOk() {
return this.sharedField; // ok because it only races with a private method
}
[thread-safety] Change meaning of @ThreadSafe to "can run in parallel with any thread including itself" Summary: Previously, annotating something ThreadSafe meant "check that it is safe to run all of this procedure's methods in parallel with each other" (including self-parallelization). This makes sense, but it means that if the user writes no annotations, we do no checking. I'm moving toward a model of inferring when an access might happen on a thread that can run concurrently with other threads, then automatically checking that it is thread-safe w.r.t to all other accesses to the same memory (on or off the current thread thread). This will let us report even when there are no `ThreadSafe` annotations. Any method that is known to run on a new thread (e.g., `Runnable.run`) will be modeled as running on a thread that can run in parallel with other threads, and so will any method that is `synchronized` or acquires a lock. In this setup, adding `ThreadSafe` to a method just means: "assume that the current method can run in parallel with any thread, including another thread that includes a different invocation of the same method (a self race) unless you see evidence to the contrary" (e.g., calling `assertMainThread` or annotating with `UiThread`). The key step in this diff is changing the threads domain to abstract *what threads the current thread may run in parallel with* rather than *what the current thread* is. This makes things much simpler. Reviewed By: jberdine Differential Revision: D5895242 fbshipit-source-id: 2e23d1e
7 years ago
Object sStaticField;
public Object FP_lazyInitOk() {
synchronized (ThreadSafeExample.class) {
if (sStaticField != null) {
sStaticField = new Object();
}
}
return sStaticField; // we'll warn here, although this is fine
}
}
class ExtendsThreadSafeExample extends ThreadSafeExample {
Integer field;
/* Presently,we will warn not just on overwridden methods from
@ThreadSafe class, but potentially on other methods in subclass */
public void newmethodBad() {
field = 22;
}
/* Bad now that it's overridden */
public void tsOK() {
field = 44;
}
}
@NotThreadSafe
class NotThreadSafeExtendsThreadSafeExample extends ThreadSafeExample {
Integer field;
/* We don't want to warn on this */
public void newmethodBad() {
field = 22;
}
}
@ThreadSafe
class YesThreadSafeExtendsNotThreadSafeExample extends NotThreadSafeExtendsThreadSafeExample {
Integer subsubfield;
/* We do want to warn on this */
public void subsubmethodBad() {
subsubfield = 22;
}
}
[thread-safety] Change meaning of @ThreadSafe to "can run in parallel with any thread including itself" Summary: Previously, annotating something ThreadSafe meant "check that it is safe to run all of this procedure's methods in parallel with each other" (including self-parallelization). This makes sense, but it means that if the user writes no annotations, we do no checking. I'm moving toward a model of inferring when an access might happen on a thread that can run concurrently with other threads, then automatically checking that it is thread-safe w.r.t to all other accesses to the same memory (on or off the current thread thread). This will let us report even when there are no `ThreadSafe` annotations. Any method that is known to run on a new thread (e.g., `Runnable.run`) will be modeled as running on a thread that can run in parallel with other threads, and so will any method that is `synchronized` or acquires a lock. In this setup, adding `ThreadSafe` to a method just means: "assume that the current method can run in parallel with any thread, including another thread that includes a different invocation of the same method (a self race) unless you see evidence to the contrary" (e.g., calling `assertMainThread` or annotating with `UiThread`). The key step in this diff is changing the threads domain to abstract *what threads the current thread may run in parallel with* rather than *what the current thread* is. This makes things much simpler. Reviewed By: jberdine Differential Revision: D5895242 fbshipit-source-id: 2e23d1e
7 years ago
class Unannotated {
int mField;
// although ThreadSafeExample is annotated @ThreadSafe, mutating fields of this class in a
// non-threadsafe context should be allowed
void callThreadSafeAnnotatedCode1Ok(ThreadSafeExample o) {
o.f = null;
}
void callThreadSafeAnnotatedCode2Ok(ThreadSafeExample o) {
o.tsBad();
}
void mutateMyFieldOk() {
this.mField = 1;
}
}