1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.camera.async; 18 19 import java.util.ArrayList; 20 import java.util.HashSet; 21 import java.util.List; 22 import java.util.Set; 23 24 /** 25 * Enables handling the shut-down of components in a structured way. 26 * <p> 27 * Lifetimes are nestable sets of {@link SafeCloseable}s useful for guaranteeing 28 * that resources, such as threads, files, hardware devices, etc., are properly 29 * closed when necessary. 30 * <p> 31 * Child lifetimes are closed when their parent is closed, or when they are 32 * closed directly, whichever comes first. Objects added to a particular 33 * lifetime will only ever be closed once by that lifetime. 34 * </p> 35 */ 36 public class Lifetime implements SafeCloseable { 37 /** 38 * The parent, or null if there is no parent lifetime. 39 */ 40 private final Lifetime mParent; 41 private final Object mLock; 42 private final Set<SafeCloseable> mCloseables; 43 private boolean mClosed; 44 45 public Lifetime() { 46 mLock = new Object(); 47 mCloseables = new HashSet<SafeCloseable>(); 48 mParent = null; 49 mClosed = false; 50 } 51 52 public Lifetime(Lifetime parent) { 53 mLock = new Object(); 54 mCloseables = new HashSet<SafeCloseable>(); 55 mParent = parent; 56 mClosed = false; 57 parent.mCloseables.add(this); 58 } 59 60 /** 61 * Adds the given object to this lifetime and returns it. 62 */ 63 public <T extends SafeCloseable> T add(T closeable) { 64 boolean needToClose = false; 65 synchronized (mLock) { 66 if (mClosed) { 67 needToClose = true; 68 } else { 69 mCloseables.add(closeable); 70 } 71 } 72 if (needToClose) { 73 closeable.close(); 74 } 75 return closeable; 76 } 77 78 @Override 79 public void close() { 80 List<SafeCloseable> toClose = new ArrayList<SafeCloseable>(); 81 synchronized (mLock) { 82 if (mClosed) { 83 return; 84 } 85 mClosed = true; 86 // Remove from parent to avoid leaking memory if a long-lasting 87 // lifetime has lots of shorter-lived lifetimes created and 88 // destroyed repeatedly. 89 if (mParent != null) { 90 mParent.mCloseables.remove(this); 91 } 92 toClose.addAll(mCloseables); 93 mCloseables.clear(); 94 } 95 // Invoke close() outside the critical section 96 for (SafeCloseable closeable : toClose) { 97 closeable.close(); 98 } 99 } 100 } 101