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.

227 lines
4.1 KiB

/*
* Copyright (c) 2016 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package codetoanalyze.java.checkers;
import javax.annotation.concurrent.ThreadSafe;
class OurThreadUtils{
native static boolean isMainThread();
static void assertMainThread(){}
static void assertHoldsLock(Object lock){}
}
class OurThreadUtil{ /*This is like AndroidThreadUtil*/
native static boolean isUiThread();
static void assertOnUiThread(){}
static void assertOnBackgroundThread(){}
}
@ThreadSafe
class RaceWithMainThread{
Integer f;
void main_thread_OK(){
OurThreadUtils.assertMainThread();
f = 88;
}
Integer f1;
void main_thread1_OK(){
OurThreadUtil.assertOnUiThread();
f1 = 88;
}
void main_thread_indirect_OK() {
main_thread_OK();
f = 77;
}
void read_from_main_thread_OK(){
Integer x;
OurThreadUtils.assertMainThread();
x = f;
}
void read_unprotected_unthreaded_Bad(){
Integer x;
x = f;
}
void read_unprotected_unthreaded1_Bad(){
Integer x;
x = f1;
}
/*There is a particularly subtle idiom which avoids races, where a
variable can be read without protection on the main thread, if
it is written with protection on the main thread and read with
protection off. The next three methods do this safely, and the fourth
unsafely.
*/
Integer i;
void protected_write_on_main_thread_OK() {
OurThreadUtils.assertMainThread();
synchronized (this) {
i = 99;
}
}
void unprotected_read_on_main_thread_OK() {
Integer x;
OurThreadUtils.assertMainThread();
x = i;
}
void protected_read_off_main_thread_OK() {
Integer x;
synchronized (this) {
x = i;
}
}
void readProtectedUnthreadedBad(){
Integer x;
synchronized (this){
x = f;
}
}
Integer g;
void holds_lock_OK(){
OurThreadUtils.assertHoldsLock(this);
g = 88;
}
void holds_lock_indirect_OK() {
holds_lock_OK();
g = 77;
}
Integer ff;
void conditional1_Ok(boolean b){
if (b)
{ /*People not literally putting this assert inside if's,
but implicitly by method calls */
OurThreadUtils.assertMainThread();
ff = 88;
}
}
[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
void conditional2_bad(boolean b){
if (b)
{
OurThreadUtils.assertMainThread();
ff = 88;
} else {
[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
ff = 99; // this might or might now run on the main thread; warn
}
}
void conditional_isMainThread_Ok(){
if (OurThreadUtils.isMainThread())
{
ff = 88;
}
}
void conditional_isUiThread_Ok(){
if (OurThreadUtil.isUiThread())
{
ff = 88;
}
}
void conditional_isMainThread_ElseBranch_Bad(){
if (OurThreadUtils.isMainThread())
{
synchronized(this){
ff = 88;
}
} else {
ff = 99;
}
}
void conditional_isUiThread_ElseBranch_Bad(){
if (OurThreadUtil.isUiThread())
{
synchronized(this){
ff = 88;
}
} else {
ff = 99;
}
}
void conditional_isMainThread_Negation_Bad(){
if (!OurThreadUtils.isMainThread())
{
ff = 88;
}
}
void conditional_isMainThread_ElseBranch_Ok(){
if (!OurThreadUtils.isMainThread())
{
synchronized(this){
ff = 88;
}
} else {
ff = 99;
}
}
Object mFld;
public void confusedAssertBad(boolean b) {
if (b) {
OurThreadUtil.assertOnBackgroundThread();
} else {
OurThreadUtil.assertOnUiThread();
}
// not sure if we're on UI or background, should report
mFld = null;
}
}
[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
// not marked thread-safe
class Unmarked {
int mField;
void writeOnUiThreadOk() {
OurThreadUtil.assertOnUiThread();
mField = 7;
}
int readOnUiThreadOk() {
OurThreadUtil.assertOnUiThread();
return mField;
}
int readOffUiThreadOk() {
// even though this read isn't known to be on the UI thread, we shouldn't assume that it occurs
// on a background thread
return mField;
}
}