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    *   https://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.ObjectUtil;
19  import io.netty.util.internal.StringUtil;
20  import io.netty.util.internal.logging.InternalLogger;
21  import io.netty.util.internal.logging.InternalLoggerFactory;
22  
23  /**
24   * Collection of method to handle objects that may implement {@link ReferenceCounted}.
25   */
26  public final class ReferenceCountUtil {
27  
28      private static final InternalLogger logger = InternalLoggerFactory.getInstance(ReferenceCountUtil.class);
29  
30      static {
31          ResourceLeakDetector.addExclusions(ReferenceCountUtil.class, "touch");
32      }
33  
34      /**
35       * Try to call {@link ReferenceCounted#retain()} if the specified message implements {@link ReferenceCounted}.
36       * If the specified message doesn't implement {@link ReferenceCounted}, this method does nothing.
37       */
38      @SuppressWarnings("unchecked")
39      public static <T> T retain(T msg) {
40          if (msg instanceof ReferenceCounted) {
41              return (T) ((ReferenceCounted) msg).retain();
42          }
43          return msg;
44      }
45  
46      /**
47       * Try to call {@link ReferenceCounted#retain(int)} if the specified message implements {@link ReferenceCounted}.
48       * If the specified message doesn't implement {@link ReferenceCounted}, this method does nothing.
49       */
50      @SuppressWarnings("unchecked")
51      public static <T> T retain(T msg, int increment) {
52          ObjectUtil.checkPositive(increment, "increment");
53          if (msg instanceof ReferenceCounted) {
54              return (T) ((ReferenceCounted) msg).retain(increment);
55          }
56          return msg;
57      }
58  
59      /**
60       * Tries to call {@link ReferenceCounted#touch()} if the specified message implements {@link ReferenceCounted}.
61       * If the specified message doesn't implement {@link ReferenceCounted}, this method does nothing.
62       */
63      @SuppressWarnings("unchecked")
64      public static <T> T touch(T msg) {
65          if (msg instanceof ReferenceCounted) {
66              return (T) ((ReferenceCounted) msg).touch();
67          }
68          return msg;
69      }
70  
71      /**
72       * Tries to call {@link ReferenceCounted#touch(Object)} if the specified message implements
73       * {@link ReferenceCounted}.  If the specified message doesn't implement {@link ReferenceCounted},
74       * this method does nothing.
75       */
76      @SuppressWarnings("unchecked")
77      public static <T> T touch(T msg, Object hint) {
78          if (msg instanceof ReferenceCounted) {
79              return (T) ((ReferenceCounted) msg).touch(hint);
80          }
81          return msg;
82      }
83  
84      /**
85       * Try to call {@link ReferenceCounted#release()} if the specified message implements {@link ReferenceCounted}.
86       * If the specified message doesn't implement {@link ReferenceCounted}, this method does nothing.
87       */
88      public static boolean release(Object msg) {
89          if (msg instanceof ReferenceCounted) {
90              return ((ReferenceCounted) msg).release();
91          }
92          return false;
93      }
94  
95      /**
96       * Try to call {@link ReferenceCounted#release(int)} if the specified message implements {@link ReferenceCounted}.
97       * If the specified message doesn't implement {@link ReferenceCounted}, this method does nothing.
98       */
99      public static boolean release(Object msg, int decrement) {
100         ObjectUtil.checkPositive(decrement, "decrement");
101         if (msg instanceof ReferenceCounted) {
102             return ((ReferenceCounted) msg).release(decrement);
103         }
104         return false;
105     }
106 
107     /**
108      * Try to call {@link ReferenceCounted#release()} if the specified message implements {@link ReferenceCounted}.
109      * If the specified message doesn't implement {@link ReferenceCounted}, this method does nothing.
110      * Unlike {@link #release(Object)} this method catches an exception raised by {@link ReferenceCounted#release()}
111      * and logs it, rather than rethrowing it to the caller.  It is usually recommended to use {@link #release(Object)}
112      * instead, unless you absolutely need to swallow an exception.
113      */
114     public static void safeRelease(Object msg) {
115         try {
116             release(msg);
117         } catch (Throwable t) {
118             logger.warn("Failed to release a message: {}", msg, t);
119         }
120     }
121 
122     /**
123      * Try to call {@link ReferenceCounted#release(int)} if the specified message implements {@link ReferenceCounted}.
124      * If the specified message doesn't implement {@link ReferenceCounted}, this method does nothing.
125      * Unlike {@link #release(Object)} this method catches an exception raised by {@link ReferenceCounted#release(int)}
126      * and logs it, rather than rethrowing it to the caller.  It is usually recommended to use
127      * {@link #release(Object, int)} instead, unless you absolutely need to swallow an exception.
128      */
129     public static void safeRelease(Object msg, int decrement) {
130         try {
131             ObjectUtil.checkPositive(decrement, "decrement");
132             release(msg, decrement);
133         } catch (Throwable t) {
134             if (logger.isWarnEnabled()) {
135                 logger.warn("Failed to release a message: {} (decrement: {})", msg, decrement, t);
136             }
137         }
138     }
139 
140     /**
141      * Schedules the specified object to be released when the caller thread terminates. Note that this operation is
142      * intended to simplify reference counting of ephemeral objects during unit tests. Do not use it beyond the
143      * intended use case.
144      *
145      * @deprecated this may introduce a lot of memory usage so it is generally preferable to manually release objects.
146      */
147     @Deprecated
148     public static <T> T releaseLater(T msg) {
149         return releaseLater(msg, 1);
150     }
151 
152     /**
153      * Schedules the specified object to be released when the caller thread terminates. Note that this operation is
154      * intended to simplify reference counting of ephemeral objects during unit tests. Do not use it beyond the
155      * intended use case.
156      *
157      * @deprecated this may introduce a lot of memory usage so it is generally preferable to manually release objects.
158      */
159     @Deprecated
160     public static <T> T releaseLater(T msg, int decrement) {
161         ObjectUtil.checkPositive(decrement, "decrement");
162         if (msg instanceof ReferenceCounted) {
163             ThreadDeathWatcher.watch(Thread.currentThread(), new ReleasingTask((ReferenceCounted) msg, decrement));
164         }
165         return msg;
166     }
167 
168     /**
169      * Returns reference count of a {@link ReferenceCounted} object. If object is not type of
170      * {@link ReferenceCounted}, {@code -1} is returned.
171      */
172     public static int refCnt(Object msg) {
173         return msg instanceof ReferenceCounted ? ((ReferenceCounted) msg).refCnt() : -1;
174     }
175 
176     /**
177      * Releases the objects when the thread that called {@link #releaseLater(Object)} has been terminated.
178      */
179     private static final class ReleasingTask implements Runnable {
180 
181         private final ReferenceCounted obj;
182         private final int decrement;
183 
184         ReleasingTask(ReferenceCounted obj, int decrement) {
185             this.obj = obj;
186             this.decrement = decrement;
187         }
188 
189         @Override
190         public void run() {
191             try {
192                 if (!obj.release(decrement)) {
193                     logger.warn("Non-zero refCnt: {}", this);
194                 } else {
195                     logger.debug("Released: {}", this);
196                 }
197             } catch (Exception ex) {
198                 logger.warn("Failed to release an object: {}", obj, ex);
199             }
200         }
201 
202         @Override
203         public String toString() {
204             return StringUtil.simpleClassName(obj) + ".release(" + decrement + ") refCnt: " + obj.refCnt();
205         }
206     }
207 
208     private ReferenceCountUtil() { }
209 }