1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty5.channel;
17
18 import io.netty5.util.concurrent.FastThreadLocal;
19 import io.netty5.util.internal.PlatformDependent;
20 import io.netty5.util.internal.logging.InternalLogger;
21 import io.netty5.util.internal.logging.InternalLoggerFactory;
22
23 import java.lang.annotation.ElementType;
24 import java.lang.annotation.Inherited;
25 import java.lang.annotation.Retention;
26 import java.lang.annotation.RetentionPolicy;
27 import java.lang.annotation.Target;
28 import java.lang.reflect.Method;
29 import java.net.SocketAddress;
30 import java.security.AccessController;
31 import java.security.PrivilegedExceptionAction;
32 import java.util.Map;
33 import java.util.WeakHashMap;
34
35 final class ChannelHandlerMask {
36 private static final InternalLogger logger = InternalLoggerFactory.getInstance(ChannelHandlerMask.class);
37
38
39 static final int MASK_CHANNEL_EXCEPTION_CAUGHT = 1;
40 static final int MASK_CHANNEL_REGISTERED = 1 << 1;
41 static final int MASK_CHANNEL_UNREGISTERED = 1 << 2;
42 static final int MASK_CHANNEL_ACTIVE = 1 << 3;
43 static final int MASK_CHANNEL_INACTIVE = 1 << 4;
44 static final int MASK_CHANNEL_SHUTDOWN = 1 << 5;
45 static final int MASK_CHANNEL_READ = 1 << 6;
46 static final int MASK_CHANNEL_READ_COMPLETE = 1 << 7;
47 static final int MASK_CHANNEL_INBOUND_EVENT = 1 << 8;
48 static final int MASK_CHANNEL_WRITABILITY_CHANGED = 1 << 9;
49 static final int MASK_BIND = 1 << 10;
50 static final int MASK_CONNECT = 1 << 11;
51 static final int MASK_DISCONNECT = 1 << 12;
52 static final int MASK_CLOSE = 1 << 13;
53 static final int MASK_SHUTDOWN = 1 << 14;
54 static final int MASK_REGISTER = 1 << 15;
55 static final int MASK_DEREGISTER = 1 << 16;
56 static final int MASK_READ = 1 << 17;
57 static final int MASK_WRITE = 1 << 18;
58 static final int MASK_FLUSH = 1 << 19;
59 static final int MASK_SEND_OUTBOUND_EVENT = 1 << 20;
60
61 static final int MASK_PENDING_OUTBOUND_BYTES = 1 << 21;
62
63 private static final int MASK_ALL_INBOUND = MASK_CHANNEL_EXCEPTION_CAUGHT | MASK_CHANNEL_REGISTERED |
64 MASK_CHANNEL_UNREGISTERED | MASK_CHANNEL_ACTIVE | MASK_CHANNEL_INACTIVE | MASK_CHANNEL_SHUTDOWN |
65 MASK_CHANNEL_READ | MASK_CHANNEL_READ_COMPLETE | MASK_CHANNEL_WRITABILITY_CHANGED |
66 MASK_CHANNEL_INBOUND_EVENT;
67 private static final int MASK_ALL_OUTBOUND = MASK_BIND | MASK_CONNECT | MASK_DISCONNECT |
68 MASK_CLOSE | MASK_SHUTDOWN | MASK_REGISTER | MASK_DEREGISTER | MASK_READ | MASK_WRITE | MASK_FLUSH |
69 MASK_SEND_OUTBOUND_EVENT | MASK_PENDING_OUTBOUND_BYTES;
70
71 private static final FastThreadLocal<Map<Class<? extends ChannelHandler>, Integer>> MASKS =
72 new FastThreadLocal<>() {
73 @Override
74 protected Map<Class<? extends ChannelHandler>, Integer> initialValue() {
75 return new WeakHashMap<>(32);
76 }
77 };
78
79
80
81
82 static int mask(Class<? extends ChannelHandler> clazz) {
83
84
85 Map<Class<? extends ChannelHandler>, Integer> cache = MASKS.get();
86 Integer mask = cache.get(clazz);
87 if (mask == null) {
88 mask = mask0(clazz);
89 cache.put(clazz, mask);
90 }
91 return mask;
92 }
93
94 static boolean isInbound(Class<? extends ChannelHandler> clazz) {
95 return (mask(clazz) & MASK_ALL_INBOUND) != 0;
96 }
97
98 static boolean isOutbound(Class<? extends ChannelHandler> clazz) {
99 return (mask(clazz) & MASK_ALL_OUTBOUND) != 0;
100 }
101
102
103
104
105 private static int mask0(Class<? extends ChannelHandler> handlerType) {
106 int mask = 0;
107 mask |= MASK_ALL_INBOUND;
108 mask |= MASK_ALL_OUTBOUND;
109
110 try {
111 if (isSkippable(handlerType, "channelExceptionCaught", ChannelHandlerContext.class, Throwable.class)) {
112 mask &= ~MASK_CHANNEL_EXCEPTION_CAUGHT;
113 }
114
115 if (isSkippable(handlerType, "channelRegistered", ChannelHandlerContext.class)) {
116 mask &= ~MASK_CHANNEL_REGISTERED;
117 }
118 if (isSkippable(handlerType, "channelUnregistered", ChannelHandlerContext.class)) {
119 mask &= ~MASK_CHANNEL_UNREGISTERED;
120 }
121 if (isSkippable(handlerType, "channelActive", ChannelHandlerContext.class)) {
122 mask &= ~MASK_CHANNEL_ACTIVE;
123 }
124 if (isSkippable(handlerType, "channelInactive", ChannelHandlerContext.class)) {
125 mask &= ~MASK_CHANNEL_INACTIVE;
126 }
127 if (isSkippable(handlerType, "channelShutdown", ChannelHandlerContext.class,
128 ChannelShutdownDirection.class)) {
129 mask &= ~MASK_CHANNEL_SHUTDOWN;
130 }
131 if (isSkippable(handlerType, "channelRead", ChannelHandlerContext.class, Object.class)) {
132 mask &= ~MASK_CHANNEL_READ;
133 }
134 if (isSkippable(handlerType, "channelReadComplete", ChannelHandlerContext.class)) {
135 mask &= ~MASK_CHANNEL_READ_COMPLETE;
136 }
137 if (isSkippable(handlerType, "channelWritabilityChanged", ChannelHandlerContext.class)) {
138 mask &= ~MASK_CHANNEL_WRITABILITY_CHANGED;
139 }
140 if (isSkippable(handlerType, "channelInboundEvent", ChannelHandlerContext.class, Object.class)) {
141 mask &= ~MASK_CHANNEL_INBOUND_EVENT;
142 }
143 if (isSkippable(handlerType, "bind", ChannelHandlerContext.class, SocketAddress.class)) {
144 mask &= ~MASK_BIND;
145 }
146 if (isSkippable(handlerType, "connect", ChannelHandlerContext.class, SocketAddress.class,
147 SocketAddress.class)) {
148 mask &= ~MASK_CONNECT;
149 }
150 if (isSkippable(handlerType, "disconnect", ChannelHandlerContext.class)) {
151 mask &= ~MASK_DISCONNECT;
152 }
153 if (isSkippable(handlerType, "close", ChannelHandlerContext.class)) {
154 mask &= ~MASK_CLOSE;
155 }
156 if (isSkippable(handlerType, "shutdown", ChannelHandlerContext.class,
157 ChannelShutdownDirection.class)) {
158 mask &= ~MASK_SHUTDOWN;
159 }
160 if (isSkippable(handlerType, "register", ChannelHandlerContext.class)) {
161 mask &= ~MASK_REGISTER;
162 }
163 if (isSkippable(handlerType, "deregister", ChannelHandlerContext.class)) {
164 mask &= ~MASK_DEREGISTER;
165 }
166 if (isSkippable(handlerType, "read", ChannelHandlerContext.class)) {
167 mask &= ~MASK_READ;
168 }
169 if (isSkippable(handlerType, "write", ChannelHandlerContext.class, Object.class)) {
170 mask &= ~MASK_WRITE;
171 }
172 if (isSkippable(handlerType, "flush", ChannelHandlerContext.class)) {
173 mask &= ~MASK_FLUSH;
174 }
175 if (isSkippable(handlerType, "sendOutboundEvent", ChannelHandlerContext.class, Object.class)) {
176 mask &= ~MASK_SEND_OUTBOUND_EVENT;
177 }
178 if (isSkippable(handlerType, "pendingOutboundBytes", ChannelHandlerContext.class)) {
179 mask &= ~MASK_PENDING_OUTBOUND_BYTES;
180 }
181 } catch (Exception e) {
182
183 PlatformDependent.throwException(e);
184 }
185
186 return mask;
187 }
188
189 private static boolean isSkippable(
190 final Class<?> handlerType, final String methodName, final Class<?>... paramTypes) throws Exception {
191 return AccessController.doPrivileged((PrivilegedExceptionAction<Boolean>) () -> {
192 Method m;
193 try {
194 m = handlerType.getMethod(methodName, paramTypes);
195 } catch (NoSuchMethodException e) {
196 if (logger.isDebugEnabled()) {
197 logger.debug(
198 "Class {} missing method {}, assume we can not skip execution", handlerType, methodName, e);
199 }
200 return false;
201 }
202 return m.isAnnotationPresent(Skip.class);
203 });
204 }
205
206 private ChannelHandlerMask() { }
207
208
209
210
211
212
213
214
215
216
217
218
219 @Target(ElementType.METHOD)
220 @Retention(RetentionPolicy.RUNTIME)
221 @interface Skip {
222
223 }
224 }