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