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 }