1 /*
2  * Copyright 2013 The Netty Project
3  *
4  * The Netty Project licenses this file to you under the Apache License,
5  * version 2.0 (the "License"); you may not use this file except in compliance
6  * with the License. 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, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations
14  * under the License.
15  */

16 package io.netty.util;
17
18 import io.netty.util.internal.StringUtil;
19 import io.netty.util.internal.logging.InternalLogger;
20 import io.netty.util.internal.logging.InternalLoggerFactory;
21
22 /**
23  * Collection of method to handle objects that may implement {@link ReferenceCounted}.
24  */

25 public final class ReferenceCountUtil {
26
27     private static final InternalLogger logger = InternalLoggerFactory.getInstance(ReferenceCountUtil.class);
28
29     static {
30         ResourceLeakDetector.addExclusions(ReferenceCountUtil.class"touch");
31     }
32
33     /**
34      * Try to call {@link ReferenceCounted#retain()} if the specified message implements {@link ReferenceCounted}.
35      * If the specified message doesn't implement {@link ReferenceCounted}, this method does nothing.
36      */

37     @SuppressWarnings("unchecked")
38     public static <T> T retain(T msg) {
39         if (msg instanceof ReferenceCounted) {
40             return (T) ((ReferenceCounted) msg).retain();
41         }
42         return msg;
43     }
44
45     /**
46      * Try to call {@link ReferenceCounted#retain(int)} if the specified message implements {@link ReferenceCounted}.
47      * If the specified message doesn't implement {@link ReferenceCounted}, this method does nothing.
48      */

49     @SuppressWarnings("unchecked")
50     public static <T> T retain(T msg, int increment) {
51         if (msg instanceof ReferenceCounted) {
52             return (T) ((ReferenceCounted) msg).retain(increment);
53         }
54         return msg;
55     }
56
57     /**
58      * Tries to call {@link ReferenceCounted#touch()} if the specified message implements {@link ReferenceCounted}.
59      * If the specified message doesn't implement {@link ReferenceCounted}, this method does nothing.
60      */

61     @SuppressWarnings("unchecked")
62     public static <T> T touch(T msg) {
63         if (msg instanceof ReferenceCounted) {
64             return (T) ((ReferenceCounted) msg).touch();
65         }
66         return msg;
67     }
68
69     /**
70      * Tries to call {@link ReferenceCounted#touch(Object)} if the specified message implements
71      * {@link ReferenceCounted}.  If the specified message doesn't implement {@link ReferenceCounted},
72      * this method does nothing.
73      */

74     @SuppressWarnings("unchecked")
75     public static <T> T touch(T msg, Object hint) {
76         if (msg instanceof ReferenceCounted) {
77             return (T) ((ReferenceCounted) msg).touch(hint);
78         }
79         return msg;
80     }
81
82     /**
83      * Try to call {@link ReferenceCounted#release()} if the specified message implements {@link ReferenceCounted}.
84      * If the specified message doesn't implement {@link ReferenceCounted}, this method does nothing.
85      */

86     public static boolean release(Object msg) {
87         if (msg instanceof ReferenceCounted) {
88             return ((ReferenceCounted) msg).release();
89         }
90         return false;
91     }
92
93     /**
94      * Try to call {@link ReferenceCounted#release(int)} if the specified message implements {@link ReferenceCounted}.
95      * If the specified message doesn't implement {@link ReferenceCounted}, this method does nothing.
96      */

97     public static boolean release(Object msg, int decrement) {
98         if (msg instanceof ReferenceCounted) {
99             return ((ReferenceCounted) msg).release(decrement);
100         }
101         return false;
102     }
103
104     /**
105      * Try to call {@link ReferenceCounted#release()} if the specified message implements {@link ReferenceCounted}.
106      * If the specified message doesn't implement {@link ReferenceCounted}, this method does nothing.
107      * Unlike {@link #release(Object)} this method catches an exception raised by {@link ReferenceCounted#release()}
108      * and logs it, rather than rethrowing it to the caller.  It is usually recommended to use {@link #release(Object)}
109      * instead, unless you absolutely need to swallow an exception.
110      */

111     public static void safeRelease(Object msg) {
112         try {
113             release(msg);
114         } catch (Throwable t) {
115             logger.warn("Failed to release a message: {}", msg, t);
116         }
117     }
118
119     /**
120      * Try to call {@link ReferenceCounted#release(int)} if the specified message implements {@link ReferenceCounted}.
121      * If the specified message doesn't implement {@link ReferenceCounted}, this method does nothing.
122      * Unlike {@link #release(Object)} this method catches an exception raised by {@link ReferenceCounted#release(int)}
123      * and logs it, rather than rethrowing it to the caller.  It is usually recommended to use
124      * {@link #release(Object, int)} instead, unless you absolutely need to swallow an exception.
125      */

126     public static void safeRelease(Object msg, int decrement) {
127         try {
128             release(msg, decrement);
129         } catch (Throwable t) {
130             if (logger.isWarnEnabled()) {
131                 logger.warn("Failed to release a message: {} (decrement: {})", msg, decrement, t);
132             }
133         }
134     }
135
136     /**
137      * Schedules the specified object to be released when the caller thread terminates. Note that this operation is
138      * intended to simplify reference counting of ephemeral objects during unit tests. Do not use it beyond the
139      * intended use case.
140      *
141      * @deprecated this may introduce a lot of memory usage so it is generally preferable to manually release objects.
142      */

143     @Deprecated
144     public static <T> T releaseLater(T msg) {
145         return releaseLater(msg, 1);
146     }
147
148     /**
149      * Schedules the specified object to be released when the caller thread terminates. Note that this operation is
150      * intended to simplify reference counting of ephemeral objects during unit tests. Do not use it beyond the
151      * intended use case.
152      *
153      * @deprecated this may introduce a lot of memory usage so it is generally preferable to manually release objects.
154      */

155     @Deprecated
156     public static <T> T releaseLater(T msg, int decrement) {
157         if (msg instanceof ReferenceCounted) {
158             ThreadDeathWatcher.watch(Thread.currentThread(), new ReleasingTask((ReferenceCounted) msg, decrement));
159         }
160         return msg;
161     }
162
163     /**
164      * Returns reference count of a {@link ReferenceCounted} object. If object is not type of
165      * {@link ReferenceCounted}, {@code -1} is returned.
166      */

167     public static int refCnt(Object msg) {
168         return msg instanceof ReferenceCounted ? ((ReferenceCounted) msg).refCnt() : -1;
169     }
170
171     /**
172      * Releases the objects when the thread that called {@link #releaseLater(Object)} has been terminated.
173      */

174     private static final class ReleasingTask implements Runnable {
175
176         private final ReferenceCounted obj;
177         private final int decrement;
178
179         ReleasingTask(ReferenceCounted obj, int decrement) {
180             this.obj = obj;
181             this.decrement = decrement;
182         }
183
184         @Override
185         public void run() {
186             try {
187                 if (!obj.release(decrement)) {
188                     logger.warn("Non-zero refCnt: {}"this);
189                 } else {
190                     logger.debug("Released: {}"this);
191                 }
192             } catch (Exception ex) {
193                 logger.warn("Failed to release an object: {}", obj, ex);
194             }
195         }
196
197         @Override
198         public String toString() {
199             return StringUtil.simpleClassName(obj) + ".release(" + decrement + ") refCnt: " + obj.refCnt();
200         }
201     }
202
203     private ReferenceCountUtil() { }
204 }
205