Prevent leaking of the Thread.interrupt flag to other LoadTasks
PiperOrigin-RevId: 311290214
This commit is contained in:
parent
df0d115b31
commit
30bc77f249
@ -32,6 +32,7 @@ import java.lang.annotation.Documented;
|
|||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages the background loading of {@link Loadable}s.
|
* Manages the background loading of {@link Loadable}s.
|
||||||
@ -56,6 +57,21 @@ public final class Loader implements LoaderErrorThrower {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancels the load.
|
* Cancels the load.
|
||||||
|
*
|
||||||
|
* <p>Loadable implementations should ensure that a currently executing {@link #load()} call
|
||||||
|
* will exit reasonably quickly after this method is called. The {@link #load()} call may exit
|
||||||
|
* either by returning or by throwing an {@link IOException}.
|
||||||
|
*
|
||||||
|
* <p>If there is a currently executing {@link #load()} call, then the thread on which that call
|
||||||
|
* is being made will be interrupted immediately after the call to this method. Hence
|
||||||
|
* implementations do not need to (and should not attempt to) interrupt the loading thread
|
||||||
|
* themselves.
|
||||||
|
*
|
||||||
|
* <p>Although the loading thread will be interrupted, Loadable implementations should not use
|
||||||
|
* the interrupted status of the loading thread in {@link #load()} to determine whether the load
|
||||||
|
* has been canceled. This approach is not robust [Internal ref: b/79223737]. Instead,
|
||||||
|
* implementations should use their own flag to signal cancelation (for example, using {@link
|
||||||
|
* AtomicBoolean}).
|
||||||
*/
|
*/
|
||||||
void cancelLoad();
|
void cancelLoad();
|
||||||
|
|
||||||
@ -307,10 +323,9 @@ public final class Loader implements LoaderErrorThrower {
|
|||||||
private static final String TAG = "LoadTask";
|
private static final String TAG = "LoadTask";
|
||||||
|
|
||||||
private static final int MSG_START = 0;
|
private static final int MSG_START = 0;
|
||||||
private static final int MSG_CANCEL = 1;
|
private static final int MSG_FINISH = 1;
|
||||||
private static final int MSG_END_OF_SOURCE = 2;
|
private static final int MSG_IO_EXCEPTION = 2;
|
||||||
private static final int MSG_IO_EXCEPTION = 3;
|
private static final int MSG_FATAL_ERROR = 3;
|
||||||
private static final int MSG_FATAL_ERROR = 4;
|
|
||||||
|
|
||||||
public final int defaultMinRetryCount;
|
public final int defaultMinRetryCount;
|
||||||
|
|
||||||
@ -321,8 +336,8 @@ public final class Loader implements LoaderErrorThrower {
|
|||||||
@Nullable private IOException currentError;
|
@Nullable private IOException currentError;
|
||||||
private int errorCount;
|
private int errorCount;
|
||||||
|
|
||||||
@Nullable private volatile Thread executorThread;
|
@Nullable private Thread executorThread;
|
||||||
private volatile boolean canceled;
|
private boolean canceled;
|
||||||
private volatile boolean released;
|
private volatile boolean released;
|
||||||
|
|
||||||
public LoadTask(Looper looper, T loadable, Loader.Callback<T> callback,
|
public LoadTask(Looper looper, T loadable, Loader.Callback<T> callback,
|
||||||
@ -354,11 +369,15 @@ public final class Loader implements LoaderErrorThrower {
|
|||||||
this.released = released;
|
this.released = released;
|
||||||
currentError = null;
|
currentError = null;
|
||||||
if (hasMessages(MSG_START)) {
|
if (hasMessages(MSG_START)) {
|
||||||
|
// The task has not been given to the executor yet.
|
||||||
|
canceled = true;
|
||||||
removeMessages(MSG_START);
|
removeMessages(MSG_START);
|
||||||
if (!released) {
|
if (!released) {
|
||||||
sendEmptyMessage(MSG_CANCEL);
|
sendEmptyMessage(MSG_FINISH);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// The task has been given to the executor.
|
||||||
|
synchronized (this) {
|
||||||
canceled = true;
|
canceled = true;
|
||||||
loadable.cancelLoad();
|
loadable.cancelLoad();
|
||||||
@Nullable Thread executorThread = this.executorThread;
|
@Nullable Thread executorThread = this.executorThread;
|
||||||
@ -366,6 +385,7 @@ public final class Loader implements LoaderErrorThrower {
|
|||||||
executorThread.interrupt();
|
executorThread.interrupt();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (released) {
|
if (released) {
|
||||||
finish();
|
finish();
|
||||||
long nowMs = SystemClock.elapsedRealtime();
|
long nowMs = SystemClock.elapsedRealtime();
|
||||||
@ -382,8 +402,12 @@ public final class Loader implements LoaderErrorThrower {
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
|
boolean shouldLoad;
|
||||||
|
synchronized (this) {
|
||||||
|
shouldLoad = !canceled;
|
||||||
executorThread = Thread.currentThread();
|
executorThread = Thread.currentThread();
|
||||||
if (!canceled) {
|
}
|
||||||
|
if (shouldLoad) {
|
||||||
TraceUtil.beginSection("load:" + loadable.getClass().getSimpleName());
|
TraceUtil.beginSection("load:" + loadable.getClass().getSimpleName());
|
||||||
try {
|
try {
|
||||||
loadable.load();
|
loadable.load();
|
||||||
@ -391,8 +415,13 @@ public final class Loader implements LoaderErrorThrower {
|
|||||||
TraceUtil.endSection();
|
TraceUtil.endSection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
synchronized (this) {
|
||||||
|
executorThread = null;
|
||||||
|
// Clear the interrupted flag if set, to avoid it leaking into a subsequent task.
|
||||||
|
Thread.interrupted();
|
||||||
|
}
|
||||||
if (!released) {
|
if (!released) {
|
||||||
sendEmptyMessage(MSG_END_OF_SOURCE);
|
sendEmptyMessage(MSG_FINISH);
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
if (!released) {
|
if (!released) {
|
||||||
@ -445,10 +474,7 @@ public final class Loader implements LoaderErrorThrower {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
switch (msg.what) {
|
switch (msg.what) {
|
||||||
case MSG_CANCEL:
|
case MSG_FINISH:
|
||||||
callback.onLoadCanceled(loadable, nowMs, durationMs, false);
|
|
||||||
break;
|
|
||||||
case MSG_END_OF_SOURCE:
|
|
||||||
try {
|
try {
|
||||||
callback.onLoadCompleted(loadable, nowMs, durationMs);
|
callback.onLoadCompleted(loadable, nowMs, durationMs);
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user