All Downloads are FREE. Search and download functionalities are using the official Maven repository.

nl.topicus.jdbc.shaded.io.grpc.internal.SharedResourceHolder Maven / Gradle / Ivy

There is a newer version: 1.1.6
Show newest version
/*
 * Copyright 2014, Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *    * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *    * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *
 *    * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package nl.topicus.jdbc.shaded.io.grpc.internal;

import nl.topicus.jdbc.shaded.com.google.nl.topicus.jdbc.shaded.com.on.base.Preconditions;
import java.util.IdentityHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import nl.topicus.jdbc.shaded.javax.annotation.concurrent.ThreadSafe;

/**
 * A holder for shared resource singletons.
 *
 * 

Components like client channels and servers need certain resources, e.g. a thread pool, to * run. If the user has not provided such resources, these nl.topicus.jdbc.shaded.com.onents will use a default one, which * is shared as a static resource. This class holds these default resources and manages their * life-cycles. * *

A resource is identified by the reference of a {@link Resource} object, which is typically a * singleton, provided to the get() and release() methods. Each Resource object (not its class) maps * to an object cached in the holder. * *

Resources are ref-counted and shut down after a delay when the ref-count reaches zero. */ @ThreadSafe public final class SharedResourceHolder { static final long DESTROY_DELAY_SECONDS = 1; // The sole holder instance. private static final SharedResourceHolder holder = new SharedResourceHolder( new ScheduledExecutorFactory() { @Override public ScheduledExecutorService createScheduledExecutor() { return Executors.newSingleThreadScheduledExecutor( GrpcUtil.getThreadFactory("grpc-shared-destroyer-%d", true)); } }); private final IdentityHashMap, Instance> instances = new IdentityHashMap, Instance>(); private final ScheduledExecutorFactory destroyerFactory; private ScheduledExecutorService destroyer; // Visible to tests that would need to create instances of the holder. SharedResourceHolder(ScheduledExecutorFactory destroyerFactory) { this.destroyerFactory = destroyerFactory; } /** * Try to get an existing instance of the given resource. If an instance does not exist, create a * new one with the given factory. * * @param resource the singleton object that identifies the requested static resource */ public static T get(Resource resource) { return holder.getInternal(resource); } /** * Releases an instance of the given resource. * *

The instance must have been obtained from {@link #get(Resource)}. Otherwise will throw * IllegalArgumentException. * *

Caller must not release a reference more than once. It's advisory that you clear the * reference to the instance with the null returned by this method. * * @param resource the singleton Resource object that identifies the released static resource * @param instance the released static resource * * @return a null which the caller can use to clear the reference to that instance. */ public static T release(final Resource resource, final T instance) { return holder.releaseInternal(resource, instance); } /** * Visible to unit tests. * * @see #get(Resource) */ @SuppressWarnings("unchecked") synchronized T getInternal(Resource resource) { Instance instance = instances.get(resource); if (instance == null) { instance = new Instance(resource.create()); instances.put(resource, instance); } if (instance.destroyTask != null) { instance.destroyTask.cancel(false); instance.destroyTask = null; } instance.refcount++; return (T) instance.payload; } /** * Visible to unit tests. */ synchronized T releaseInternal(final Resource resource, final T instance) { final Instance cached = instances.get(resource); if (cached == null) { throw new IllegalArgumentException("No cached instance found for " + resource); } Preconditions.checkArgument(instance == cached.payload, "Releasing the wrong instance"); Preconditions.checkState(cached.refcount > 0, "Refcount has already reached zero"); cached.refcount--; if (cached.refcount == 0) { 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 // nl.topicus.jdbc.shaded.com.letes. 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); } } // Always returning null return null; } /** * Defines a resource, and the way to create and destroy instances of it. */ public interface Resource { /** * Create a new instance of the resource. */ T create(); /** * Destroy the given instance. */ void close(T instance); } interface ScheduledExecutorFactory { ScheduledExecutorService createScheduledExecutor(); } private static class Instance { final Object payload; int refcount; ScheduledFuture destroyTask; Instance(Object payload) { this.payload = payload; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy