Line data Source code
1 : // Copyright (C) 2022 The Android Open Source Project 2 : // 3 : // Licensed under the Apache License, Version 2.0 (the "License"); 4 : // you may not use this file except in compliance with the License. 5 : // You may obtain a copy of the License at 6 : // 7 : // http://www.apache.org/licenses/LICENSE-2.0 8 : // 9 : // Unless required by applicable law or agreed to in writing, software 10 : // distributed under the License is distributed on an "AS IS" BASIS, 11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 : // See the License for the specific language governing permissions and 13 : // limitations under the License. 14 : 15 : package com.google.gerrit.server.util.git; 16 : 17 : import com.google.common.flogger.FluentLogger; 18 : import java.util.ArrayList; 19 : import java.util.List; 20 : import java.util.function.Supplier; 21 : 22 : /** 23 : * Pool to manage resources that need to be closed but to whom we might lose the reference to or 24 : * where closing resources individually is not always possible. 25 : * 26 : * <p>This pool can be used when we want to reuse closable resources in a multithreaded context. 27 : * Example: 28 : * 29 : * <pre>{@code 30 : * try (CloseablePool<T> pool = new CloseablePool(() -> new T())) { 31 : * for (int i = 0; i < 100; i++) { 32 : * executor.submit(() -> { 33 : * try (CloseablePool<T>.Handle handle = pool.get()) { 34 : * // Do work that might potentially take longer than the timeout. 35 : * handle.get(); // pooled instance to be used 36 : * } 37 : * }).get(1000, MILLISECONDS); 38 : * } 39 : * } 40 : * }</pre> 41 : */ 42 : public class CloseablePool<T extends AutoCloseable> implements AutoCloseable { 43 93 : private static final FluentLogger logger = FluentLogger.forEnclosingClass(); 44 : 45 : private final Supplier<T> tCreator; 46 : private List<T> ts; 47 : 48 : /** 49 : * Instantiate a new pool. The {@link Supplier} must be capable of creating a new instance on 50 : * every call. 51 : */ 52 93 : public CloseablePool(Supplier<T> tCreator) { 53 93 : this.ts = new ArrayList<>(); 54 93 : this.tCreator = tCreator; 55 93 : } 56 : 57 : /** 58 : * Get a shared instance or create a new instance. Close the returned handle to return it to the 59 : * pool. 60 : */ 61 : public synchronized Handle get() { 62 93 : if (ts.isEmpty()) { 63 93 : return new Handle(tCreator.get()); 64 : } 65 93 : return new Handle(ts.remove(ts.size() - 1)); 66 : } 67 : 68 : private synchronized boolean discard(T t) { 69 93 : if (ts != null) { 70 93 : ts.add(t); 71 93 : return true; 72 : } 73 0 : return false; 74 : } 75 : 76 : @Override 77 : public synchronized void close() { 78 93 : for (T t : ts) 79 : try { 80 93 : t.close(); 81 0 : } catch (Exception e) { 82 0 : logger.atWarning().withCause(e).log( 83 : "Failed to close resource %s in CloseablePool %s", t, this); 84 93 : } 85 93 : ts = null; 86 93 : } 87 : 88 : /** 89 : * Wrapper around an {@link AutoCloseable}. Will try to return the resource to the pool and close 90 : * it in case the pool was already closed. 91 : */ 92 : public class Handle implements AutoCloseable { 93 : private final T t; 94 : 95 93 : private Handle(T t) { 96 93 : this.t = t; 97 93 : } 98 : 99 : /** Returns the managed instance. */ 100 : public T get() { 101 93 : return t; 102 : } 103 : 104 : @Override 105 : public void close() { 106 93 : if (!discard(t)) { 107 : try { 108 0 : t.close(); 109 0 : } catch (Exception e) { 110 0 : logger.atWarning().withCause(e).log( 111 : "Failed to close resource %s in CloseablePool %s", this, CloseablePool.this); 112 0 : } 113 : } 114 93 : } 115 : } 116 : }