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