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