diff --git a/infer/src/concurrency/StarvationModels.ml b/infer/src/concurrency/StarvationModels.ml index cd8d89248..3f028c78b 100644 --- a/infer/src/concurrency/StarvationModels.ml +++ b/infer/src/concurrency/StarvationModels.ml @@ -223,6 +223,15 @@ let schedules_work_on_ui_thread = fun tenv pname -> matcher tenv pname [] +let schedules_work_on_bg_thread = + let open MethodMatcher in + let matcher = + [{default with classname= "java.lang.Object"; methods= ["scheduleGuaranteedDelayed"]}] + |> of_records + in + fun tenv pname -> matcher tenv pname [] + + type executor_thread_constraint = ForUIThread | ForNonUIThread | ForUnknownThread [@@deriving equal] diff --git a/infer/src/concurrency/StarvationModels.mli b/infer/src/concurrency/StarvationModels.mli index 6423a453f..f7f0d9534 100644 --- a/infer/src/concurrency/StarvationModels.mli +++ b/infer/src/concurrency/StarvationModels.mli @@ -57,3 +57,6 @@ val get_executor_effect : val schedules_work_on_ui_thread : Tenv.t -> Typ.Procname.t -> bool (** method call known to directly schedule work on UI thread *) + +val schedules_work_on_bg_thread : Tenv.t -> Typ.Procname.t -> bool +(** method call known to directly schedule work on BG thread *) diff --git a/infer/src/concurrency/starvation.ml b/infer/src/concurrency/starvation.ml index cfd8f2cc9..967d2f3a7 100644 --- a/infer/src/concurrency/starvation.ml +++ b/infer/src/concurrency/starvation.ml @@ -107,6 +107,9 @@ module TransferFunctions (CFG : ProcCfg.S) = struct | HilExp.AccessExpression runnable :: _ when StarvationModels.schedules_work_on_ui_thread tenv callee -> schedule_work runnable astate StarvationModels.ForUIThread + | HilExp.AccessExpression runnable :: _ + when StarvationModels.schedules_work_on_bg_thread tenv callee -> + schedule_work runnable astate StarvationModels.ForNonUIThread | _ -> astate diff --git a/infer/tests/codetoanalyze/java/starvation-whole-program/ModeledExecutors.java b/infer/tests/codetoanalyze/java/starvation-whole-program/ModeledExecutors.java index bdc5eecdf..f9dbce4f7 100644 --- a/infer/tests/codetoanalyze/java/starvation-whole-program/ModeledExecutors.java +++ b/infer/tests/codetoanalyze/java/starvation-whole-program/ModeledExecutors.java @@ -75,6 +75,46 @@ class ModeledExecutors { }, 1000L); } + + public void scheduleGuaranteedDelayedBlockingCallToNonUIThreadOk() { + Executors.scheduleGuaranteedDelayed( + new Runnable() { + @Override + public void run() { + doTransact(); + } + }, + 1000L, + 1000L); + } + + Object monitorA, monitorB; + + public void scheduleGuaranteedDelayedDeadlockBad() { + Executors.scheduleGuaranteedDelayed( + new Runnable() { + @Override + public void run() { + synchronized (monitorA) { + synchronized (monitorB) { + } + } + } + }, + 1000L, + 1000L); + + Executors.runOnUiThread( + new Runnable() { + @Override + public void run() { + synchronized (monitorB) { + synchronized (monitorA) { + } + } + } + }); + } } // modeled executors @@ -96,4 +136,7 @@ class Executors { public static void runOnUiThread(Runnable runnable) {} public static void postOnUiThreadDelayed(Runnable runnable, long delayMs) {} + + public static void scheduleGuaranteedDelayed( + Runnable job, long delayMillis, long lastExecution) {} } diff --git a/infer/tests/codetoanalyze/java/starvation-whole-program/issues.exp b/infer/tests/codetoanalyze/java/starvation-whole-program/issues.exp index cc475155c..7206eea51 100644 --- a/infer/tests/codetoanalyze/java/starvation-whole-program/issues.exp +++ b/infer/tests/codetoanalyze/java/starvation-whole-program/issues.exp @@ -5,6 +5,8 @@ codetoanalyze/java/starvation-whole-program/Deadlock.java, Deadlock.postOnBGThre codetoanalyze/java/starvation-whole-program/DirectStarvation.java, DirectStarvation.postBlockingCallToUIThreadBad():void, 29, STARVATION, no_bucket, ERROR, [`void DirectStarvation.postBlockingCallToUIThreadBad()`,Method call: `void DirectStarvation$1.run()`,Method call: `void DirectStarvation.doTransact()`,calls `boolean Binder.transact(int,Parcel,Parcel,int)`] codetoanalyze/java/starvation-whole-program/IndirectStarvation.java, IndirectStarvation.postBlockingCallToBackgroundThreadAndLockBad():void, 32, STARVATION, no_bucket, ERROR, [[Trace 1] `void IndirectStarvation.postBlockingCallToBackgroundThreadAndLockBad()`,Method call: `void IndirectStarvation$1.run()`, locks `this.monitorA` in `class IndirectStarvation`,[Trace 2] `void IndirectStarvation.postBlockingCallToBackgroundThreadAndLockBad()`,Method call: `void IndirectStarvation$2.run()`, locks `this.monitorA` in `class IndirectStarvation`,Method call: `void IndirectStarvation.doTransact()`,calls `boolean Binder.transact(int,Parcel,Parcel,int)`] codetoanalyze/java/starvation-whole-program/ModeledExecutors.java, ModeledExecutors.postBlockingCallToUIThreadBad():void, 25, STARVATION, no_bucket, ERROR, [`void ModeledExecutors.postBlockingCallToUIThreadBad()`,Method call: `void ModeledExecutors$1.run()`,Method call: `void ModeledExecutors.doTransact()`,calls `boolean Binder.transact(int,Parcel,Parcel,int)`] +codetoanalyze/java/starvation-whole-program/ModeledExecutors.java, ModeledExecutors.scheduleGuaranteedDelayedDeadlockBad():void, 94, DEADLOCK, no_bucket, ERROR, [[Trace 1] `void ModeledExecutors.scheduleGuaranteedDelayedDeadlockBad()`,Method call: `void ModeledExecutors$7.run()`, locks `this.monitorA` in `class ModeledExecutors`, locks `this.monitorB` in `class ModeledExecutors`,[Trace 2] `void ModeledExecutors.scheduleGuaranteedDelayedDeadlockBad()`,Method call: `void ModeledExecutors$8.run()`, locks `this.monitorB` in `class ModeledExecutors`, locks `this.monitorA` in `class ModeledExecutors`] +codetoanalyze/java/starvation-whole-program/ModeledExecutors.java, ModeledExecutors.scheduleGuaranteedDelayedDeadlockBad():void, 107, DEADLOCK, no_bucket, ERROR, [[Trace 1] `void ModeledExecutors.scheduleGuaranteedDelayedDeadlockBad()`,Method call: `void ModeledExecutors$8.run()`, locks `this.monitorB` in `class ModeledExecutors`, locks `this.monitorA` in `class ModeledExecutors`,[Trace 2] `void ModeledExecutors.scheduleGuaranteedDelayedDeadlockBad()`,Method call: `void ModeledExecutors$7.run()`, locks `this.monitorA` in `class ModeledExecutors`, locks `this.monitorB` in `class ModeledExecutors`] codetoanalyze/java/starvation-whole-program/ModeledExecutors.java, ModeledExecutors.staticPostBlockingCallToUIThreadBad():void, 47, STARVATION, no_bucket, ERROR, [`void ModeledExecutors.staticPostBlockingCallToUIThreadBad()`,Method call: `void ModeledExecutors$3.run()`,Method call: `void ModeledExecutors.doTransact()`,calls `boolean Binder.transact(int,Parcel,Parcel,int)`] codetoanalyze/java/starvation-whole-program/ModeledExecutors.java, ModeledExecutors.staticPostDelayedBlockingCallToUIThreadBad():void, 69, STARVATION, no_bucket, ERROR, [`void ModeledExecutors.staticPostDelayedBlockingCallToUIThreadBad()`,Method call: `void ModeledExecutors$5.run()`,Method call: `void ModeledExecutors.doTransact()`,calls `boolean Binder.transact(int,Parcel,Parcel,int)`] codetoanalyze/java/starvation-whole-program/ModeledExecutors.java, ModeledExecutors.staticRunBlockingCallToUIThreadBad():void, 58, STARVATION, no_bucket, ERROR, [`void ModeledExecutors.staticRunBlockingCallToUIThreadBad()`,Method call: `void ModeledExecutors$4.run()`,Method call: `void ModeledExecutors.doTransact()`,calls `boolean Binder.transact(int,Parcel,Parcel,int)`]