View Javadoc
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 }