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 
Lifetime()45     public Lifetime() {
46         mLock = new Object();
47         mCloseables = new HashSet<SafeCloseable>();
48         mParent = null;
49         mClosed = false;
50     }
51 
Lifetime(Lifetime parent)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      */
add(T closeable)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
close()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