PageRenderTime 48ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/src/main/java/org/jboss/netty/handler/execution/OrderedMemoryAwareThreadPoolExecutor.java

https://github.com/maerqiang/netty
Java | 337 lines | 139 code | 25 blank | 173 comment | 15 complexity | bd12a73a3fd438da76ef028d0d3a9649 MD5 | raw file
  1. /*
  2. * Copyright 2009 Red Hat, Inc.
  3. *
  4. * Red Hat licenses this file to you under the Apache License, version 2.0
  5. * (the "License"); you may not use this file except in compliance with the
  6. * 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 org.jboss.netty.handler.execution;
  17. import java.util.IdentityHashMap;
  18. import java.util.LinkedList;
  19. import java.util.Set;
  20. import java.util.WeakHashMap;
  21. import java.util.concurrent.ConcurrentMap;
  22. import java.util.concurrent.Executor;
  23. import java.util.concurrent.ThreadFactory;
  24. import java.util.concurrent.TimeUnit;
  25. import org.jboss.netty.channel.Channel;
  26. import org.jboss.netty.channel.ChannelEvent;
  27. import org.jboss.netty.channel.ChannelState;
  28. import org.jboss.netty.channel.ChannelStateEvent;
  29. import org.jboss.netty.util.ObjectSizeEstimator;
  30. import org.jboss.netty.util.internal.ConcurrentIdentityWeakKeyHashMap;
  31. /**
  32. * A {@link MemoryAwareThreadPoolExecutor} which makes sure the events from the
  33. * same {@link Channel} are executed sequentially.
  34. * <p>
  35. * <b>NOTE</b>: This thread pool inherits most characteristics of its super
  36. * type, so please make sure to refer to {@link MemoryAwareThreadPoolExecutor}
  37. * to understand how it works basically.
  38. *
  39. * <h3>Event execution order</h3>
  40. *
  41. * For example, let's say there are two executor threads that handle the events
  42. * from the two channels:
  43. * <pre>
  44. * -------------------------------------&gt; Timeline ------------------------------------&gt;
  45. *
  46. * Thread X: --- Channel A (Event A1) --. .-- Channel B (Event B2) --- Channel B (Event B3) ---&gt;
  47. * \ /
  48. * X
  49. * / \
  50. * Thread Y: --- Channel B (Event B1) --' '-- Channel A (Event A2) --- Channel A (Event A3) ---&gt;
  51. * </pre>
  52. * As you see, the events from different channels are independent from each
  53. * other. That is, an event of Channel B will not be blocked by an event of
  54. * Channel A and vice versa, unless the thread pool is exhausted.
  55. * <p>
  56. * Also, it is guaranteed that the invocation will be made sequentially for the
  57. * events from the same channel. For example, the event A2 is never executed
  58. * before the event A1 is finished. (Although not recommended, if you want the
  59. * events from the same channel to be executed simultaneously, please use
  60. * {@link MemoryAwareThreadPoolExecutor} instead.)
  61. * <p>
  62. * However, it is not guaranteed that the invocation will be made by the same
  63. * thread for the same channel. The events from the same channel can be
  64. * executed by different threads. For example, the Event A2 is executed by the
  65. * thread Y while the event A1 was executed by the thread X.
  66. *
  67. * <h3>Using a different key other than {@link Channel} to maintain event order</h3>
  68. * <p>
  69. * {@link OrderedMemoryAwareThreadPoolExecutor} uses a {@link Channel} as a key
  70. * that is used for maintaining the event execution order, as explained in the
  71. * previous section. Alternatively, you can extend it to change its behavior.
  72. * For example, you can change the key to the remote IP of the peer:
  73. *
  74. * <pre>
  75. * public class RemoteAddressBasedOMATPE extends {@link OrderedMemoryAwareThreadPoolExecutor} {
  76. *
  77. * ... Constructors ...
  78. *
  79. * {@code @Override}
  80. * protected ConcurrentMap&lt;Object, Executor&gt; newChildExecutorMap() {
  81. * // The default implementation returns a special ConcurrentMap that
  82. * // uses identity comparison only (see {@link IdentityHashMap}).
  83. * // Because SocketAddress does not work with identity comparison,
  84. * // we need to employ more generic implementation.
  85. * return new ConcurrentHashMap&lt;Object, Executor&gt;
  86. * }
  87. *
  88. * protected Object getChildExecutorKey({@link ChannelEvent} e) {
  89. * // Use the IP of the remote peer as a key.
  90. * return ((InetSocketAddress) e.getChannel().getRemoteAddress()).getAddress();
  91. * }
  92. *
  93. * // Make public so that you can call from anywhere.
  94. * public boolean removeChildExecutor(Object key) {
  95. * super.removeChildExecutor(key);
  96. * }
  97. * }
  98. * </pre>
  99. *
  100. * Please be very careful of memory leak of the child executor map. You must
  101. * call {@link #removeChildExecutor(Object)} when the life cycle of the key
  102. * ends (e.g. all connections from the same IP were closed.) Also, please
  103. * keep in mind that the key can appear again after calling {@link #removeChildExecutor(Object)}
  104. * (e.g. a new connection could come in from the same old IP after removal.)
  105. * If in doubt, prune the old unused or stall keys from the child executor map
  106. * periodically:
  107. *
  108. * <pre>
  109. * RemoteAddressBasedOMATPE executor = ...;
  110. *
  111. * on every 3 seconds:
  112. *
  113. * for (Iterator&lt;Object&gt; i = executor.getChildExecutorKeySet().iterator; i.hasNext();) {
  114. * InetAddress ip = (InetAddress) i.next();
  115. * if (there is no active connection from 'ip' now &&
  116. * there has been no incoming connection from 'ip' for last 10 minutes) {
  117. * i.remove();
  118. * }
  119. * }
  120. * </pre>
  121. *
  122. * If the expected maximum number of keys is small and deterministic, you could
  123. * use a weak key map such as <a href="http://viewvc.jboss.org/cgi-bin/viewvc.cgi/jbosscache/experimental/jsr166/src/jsr166y/ConcurrentWeakHashMap.java?view=markup">ConcurrentWeakHashMap</a>
  124. * or synchronized {@link WeakHashMap} instead of managing the life cycle of the
  125. * keys by yourself.
  126. *
  127. * @author <a href="http://www.jboss.org/netty/">The Netty Project</a>
  128. * @author <a href="http://gleamynode.net/">Trustin Lee</a>
  129. * @author David M. Lloyd (david.lloyd@redhat.com)
  130. *
  131. * @version $Rev$, $Date$
  132. *
  133. * @apiviz.landmark
  134. */
  135. public class OrderedMemoryAwareThreadPoolExecutor extends
  136. MemoryAwareThreadPoolExecutor {
  137. // TODO Make OMATPE focus on the case where Channel is the key.
  138. // Add a new less-efficient TPE that allows custom key.
  139. private final ConcurrentMap<Object, Executor> childExecutors = newChildExecutorMap();
  140. /**
  141. * Creates a new instance.
  142. *
  143. * @param corePoolSize the maximum number of active threads
  144. * @param maxChannelMemorySize the maximum total size of the queued events per channel.
  145. * Specify {@code 0} to disable.
  146. * @param maxTotalMemorySize the maximum total size of the queued events for this pool
  147. * Specify {@code 0} to disable.
  148. */
  149. public OrderedMemoryAwareThreadPoolExecutor(
  150. int corePoolSize, long maxChannelMemorySize, long maxTotalMemorySize) {
  151. super(corePoolSize, maxChannelMemorySize, maxTotalMemorySize);
  152. }
  153. /**
  154. * Creates a new instance.
  155. *
  156. * @param corePoolSize the maximum number of active threads
  157. * @param maxChannelMemorySize the maximum total size of the queued events per channel.
  158. * Specify {@code 0} to disable.
  159. * @param maxTotalMemorySize the maximum total size of the queued events for this pool
  160. * Specify {@code 0} to disable.
  161. * @param keepAliveTime the amount of time for an inactive thread to shut itself down
  162. * @param unit the {@link TimeUnit} of {@code keepAliveTime}
  163. */
  164. public OrderedMemoryAwareThreadPoolExecutor(
  165. int corePoolSize, long maxChannelMemorySize, long maxTotalMemorySize,
  166. long keepAliveTime, TimeUnit unit) {
  167. super(corePoolSize, maxChannelMemorySize, maxTotalMemorySize,
  168. keepAliveTime, unit);
  169. }
  170. /**
  171. * Creates a new instance.
  172. *
  173. * @param corePoolSize the maximum number of active threads
  174. * @param maxChannelMemorySize the maximum total size of the queued events per channel.
  175. * Specify {@code 0} to disable.
  176. * @param maxTotalMemorySize the maximum total size of the queued events for this pool
  177. * Specify {@code 0} to disable.
  178. * @param keepAliveTime the amount of time for an inactive thread to shut itself down
  179. * @param unit the {@link TimeUnit} of {@code keepAliveTime}
  180. * @param threadFactory the {@link ThreadFactory} of this pool
  181. */
  182. public OrderedMemoryAwareThreadPoolExecutor(
  183. int corePoolSize, long maxChannelMemorySize, long maxTotalMemorySize,
  184. long keepAliveTime, TimeUnit unit, ThreadFactory threadFactory) {
  185. super(corePoolSize, maxChannelMemorySize, maxTotalMemorySize,
  186. keepAliveTime, unit, threadFactory);
  187. }
  188. /**
  189. * Creates a new instance.
  190. *
  191. * @param corePoolSize the maximum number of active threads
  192. * @param maxChannelMemorySize the maximum total size of the queued events per channel.
  193. * Specify {@code 0} to disable.
  194. * @param maxTotalMemorySize the maximum total size of the queued events for this pool
  195. * Specify {@code 0} to disable.
  196. * @param keepAliveTime the amount of time for an inactive thread to shut itself down
  197. * @param unit the {@link TimeUnit} of {@code keepAliveTime}
  198. * @param threadFactory the {@link ThreadFactory} of this pool
  199. * @param objectSizeEstimator the {@link ObjectSizeEstimator} of this pool
  200. */
  201. public OrderedMemoryAwareThreadPoolExecutor(
  202. int corePoolSize, long maxChannelMemorySize, long maxTotalMemorySize,
  203. long keepAliveTime, TimeUnit unit,
  204. ObjectSizeEstimator objectSizeEstimator, ThreadFactory threadFactory) {
  205. super(corePoolSize, maxChannelMemorySize, maxTotalMemorySize,
  206. keepAliveTime, unit, objectSizeEstimator, threadFactory);
  207. }
  208. protected ConcurrentMap<Object, Executor> newChildExecutorMap() {
  209. return new ConcurrentIdentityWeakKeyHashMap<Object, Executor>();
  210. }
  211. protected Object getChildExecutorKey(ChannelEvent e) {
  212. return e.getChannel();
  213. }
  214. protected Set<Object> getChildExecutorKeySet() {
  215. return childExecutors.keySet();
  216. }
  217. protected boolean removeChildExecutor(Object key) {
  218. // FIXME: Succeed only when there is no task in the ChildExecutor's queue.
  219. // Note that it will need locking which might slow down task submission.
  220. return childExecutors.remove(key) != null;
  221. }
  222. /**
  223. * Executes the specified task concurrently while maintaining the event
  224. * order.
  225. */
  226. @Override
  227. protected void doExecute(Runnable task) {
  228. if (!(task instanceof ChannelEventRunnable)) {
  229. doUnorderedExecute(task);
  230. } else {
  231. ChannelEventRunnable r = (ChannelEventRunnable) task;
  232. getChildExecutor(r.getEvent()).execute(task);
  233. }
  234. }
  235. private Executor getChildExecutor(ChannelEvent e) {
  236. Object key = getChildExecutorKey(e);
  237. Executor executor = childExecutors.get(key);
  238. if (executor == null) {
  239. executor = new ChildExecutor();
  240. Executor oldExecutor = childExecutors.putIfAbsent(key, executor);
  241. if (oldExecutor != null) {
  242. executor = oldExecutor;
  243. }
  244. }
  245. // Remove the entry when the channel closes.
  246. if (e instanceof ChannelStateEvent) {
  247. Channel channel = e.getChannel();
  248. ChannelStateEvent se = (ChannelStateEvent) e;
  249. if (se.getState() == ChannelState.OPEN &&
  250. !channel.isOpen()) {
  251. childExecutors.remove(channel);
  252. }
  253. }
  254. return executor;
  255. }
  256. @Override
  257. protected boolean shouldCount(Runnable task) {
  258. if (task instanceof ChildExecutor) {
  259. return false;
  260. }
  261. return super.shouldCount(task);
  262. }
  263. void onAfterExecute(Runnable r, Throwable t) {
  264. afterExecute(r, t);
  265. }
  266. private final class ChildExecutor implements Executor, Runnable {
  267. private final LinkedList<Runnable> tasks = new LinkedList<Runnable>();
  268. ChildExecutor() {
  269. super();
  270. }
  271. @Override
  272. public void execute(Runnable command) {
  273. boolean needsExecution;
  274. synchronized (tasks) {
  275. needsExecution = tasks.isEmpty();
  276. tasks.add(command);
  277. }
  278. if (needsExecution) {
  279. doUnorderedExecute(this);
  280. }
  281. }
  282. @Override
  283. public void run() {
  284. Thread thread = Thread.currentThread();
  285. for (;;) {
  286. final Runnable task;
  287. synchronized (tasks) {
  288. task = tasks.getFirst();
  289. }
  290. boolean ran = false;
  291. beforeExecute(thread, task);
  292. try {
  293. task.run();
  294. ran = true;
  295. onAfterExecute(task, null);
  296. } catch (RuntimeException e) {
  297. if (!ran) {
  298. onAfterExecute(task, e);
  299. }
  300. throw e;
  301. } finally {
  302. synchronized (tasks) {
  303. tasks.removeFirst();
  304. if (tasks.isEmpty()) {
  305. break;
  306. }
  307. }
  308. }
  309. }
  310. }
  311. }
  312. }