Force the immediate release of reference counted resources on AppEngine

to avoid executors backed by request-scoped threads from becoming
zombies
See
https://cloud.google.com/appengine/docs/java/javadoc/com/google/appengine/api/ThreadManager.html#currentRequestThreadFactory--
This commit is contained in:
Louis Ryan 2016-07-21 17:45:34 -07:00
parent 3a13aa5665
commit e276359f0e
1 changed files with 26 additions and 18 deletions

View File

@ -141,27 +141,35 @@ public final class SharedResourceHolder {
Preconditions.checkState(cached.refcount > 0, "Refcount has already reached zero");
cached.refcount--;
if (cached.refcount == 0) {
Preconditions.checkState(cached.destroyTask == null, "Destroy task already scheduled");
// Schedule a delayed task to destroy the resource.
if (destroyer == null) {
destroyer = destroyerFactory.createScheduledExecutor();
}
cached.destroyTask = destroyer.schedule(new LogExceptionRunnable(new Runnable() {
@Override
public void run() {
synchronized (SharedResourceHolder.this) {
// Refcount may have gone up since the task was scheduled. Re-check it.
if (cached.refcount == 0) {
resource.close(instance);
instances.remove(resource);
if (instances.isEmpty()) {
destroyer.shutdown();
destroyer = null;
if (GrpcUtil.IS_RESTRICTED_APPENGINE) {
// AppEngine must immediately release shared resources, particularly executors
// which could retain request-scoped threads which become zombies after the request
// completes.
resource.close(instance);
instances.remove(resource);
} else {
Preconditions.checkState(cached.destroyTask == null, "Destroy task already scheduled");
// Schedule a delayed task to destroy the resource.
if (destroyer == null) {
destroyer = destroyerFactory.createScheduledExecutor();
}
cached.destroyTask = destroyer.schedule(new LogExceptionRunnable(new Runnable() {
@Override
public void run() {
synchronized (SharedResourceHolder.this) {
// Refcount may have gone up since the task was scheduled. Re-check it.
if (cached.refcount == 0) {
resource.close(instance);
instances.remove(resource);
if (instances.isEmpty()) {
destroyer.shutdown();
destroyer = null;
}
}
}
}
}
}), DESTROY_DELAY_SECONDS, TimeUnit.SECONDS);
}), DESTROY_DELAY_SECONDS, TimeUnit.SECONDS);
}
}
// Always returning null
return null;