PageRenderTime 60ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/src/main/java/org/jboss/netty/channel/socket/nio/NioDatagramChannel.java

https://github.com/com-open-source/netty
Java | 358 lines | 230 code | 49 blank | 79 comment | 31 complexity | cfb0028c12529dd230bf30045737a851 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.channel.socket.nio;
  17. import static org.jboss.netty.channel.Channels.*;
  18. import java.io.IOException;
  19. import java.net.InetAddress;
  20. import java.net.InetSocketAddress;
  21. import java.net.NetworkInterface;
  22. import java.net.SocketAddress;
  23. import java.nio.channels.DatagramChannel;
  24. import java.util.Queue;
  25. import java.util.concurrent.atomic.AtomicBoolean;
  26. import java.util.concurrent.atomic.AtomicInteger;
  27. import org.jboss.netty.buffer.ChannelBuffer;
  28. import org.jboss.netty.channel.AbstractChannel;
  29. import org.jboss.netty.channel.Channel;
  30. import org.jboss.netty.channel.ChannelException;
  31. import org.jboss.netty.channel.ChannelFactory;
  32. import org.jboss.netty.channel.ChannelFuture;
  33. import org.jboss.netty.channel.ChannelPipeline;
  34. import org.jboss.netty.channel.ChannelSink;
  35. import org.jboss.netty.channel.MessageEvent;
  36. import org.jboss.netty.channel.socket.DatagramChannelConfig;
  37. import org.jboss.netty.channel.socket.nio.SocketSendBufferPool.SendBuffer;
  38. import org.jboss.netty.util.internal.LinkedTransferQueue;
  39. import org.jboss.netty.util.internal.ThreadLocalBoolean;
  40. /**
  41. * Provides an NIO based {@link org.jboss.netty.channel.socket.DatagramChannel}.
  42. *
  43. * @author <a href="http://www.jboss.org/netty/">The Netty Project</a>
  44. * @author <a href="http://gleamynode.net/">Trustin Lee</a>
  45. * @author Daniel Bevenius (dbevenius@jboss.com)
  46. *
  47. * @version $Rev$, $Date$
  48. */
  49. class NioDatagramChannel extends AbstractChannel
  50. implements org.jboss.netty.channel.socket.DatagramChannel {
  51. /**
  52. * The {@link DatagramChannelConfig}.
  53. */
  54. private final NioDatagramChannelConfig config;
  55. /**
  56. * The {@link NioDatagramWorker} for this NioDatagramChannnel.
  57. */
  58. final NioDatagramWorker worker;
  59. /**
  60. * The {@link DatagramChannel} that this channel uses.
  61. */
  62. private final java.nio.channels.DatagramChannel datagramChannel;
  63. /**
  64. * Monitor object to synchronize access to InterestedOps.
  65. */
  66. final Object interestOpsLock = new Object();
  67. /**
  68. * Monitor object for synchronizing access to the {@link WriteRequestQueue}.
  69. */
  70. final Object writeLock = new Object();
  71. /**
  72. * WriteTask that performs write operations.
  73. */
  74. final Runnable writeTask = new WriteTask();
  75. /**
  76. * Indicates if there is a {@link WriteTask} in the task queue.
  77. */
  78. final AtomicBoolean writeTaskInTaskQueue = new AtomicBoolean();
  79. /**
  80. * Queue of write {@link MessageEvent}s.
  81. */
  82. final Queue<MessageEvent> writeBufferQueue = new WriteRequestQueue();
  83. /**
  84. * Keeps track of the number of bytes that the {@link WriteRequestQueue} currently
  85. * contains.
  86. */
  87. final AtomicInteger writeBufferSize = new AtomicInteger();
  88. /**
  89. * Keeps track of the highWaterMark.
  90. */
  91. final AtomicInteger highWaterMarkCounter = new AtomicInteger();
  92. /**
  93. * The current write {@link MessageEvent}
  94. */
  95. MessageEvent currentWriteEvent;
  96. SendBuffer currentWriteBuffer;
  97. /**
  98. * Boolean that indicates that write operation is in progress.
  99. */
  100. boolean inWriteNowLoop;
  101. boolean writeSuspended;
  102. private volatile InetSocketAddress localAddress;
  103. volatile InetSocketAddress remoteAddress;
  104. NioDatagramChannel(final ChannelFactory factory,
  105. final ChannelPipeline pipeline, final ChannelSink sink,
  106. final NioDatagramWorker worker) {
  107. super(null, factory, pipeline, sink);
  108. this.worker = worker;
  109. datagramChannel = openNonBlockingChannel();
  110. config = new DefaultNioDatagramChannelConfig(datagramChannel.socket());
  111. fireChannelOpen(this);
  112. }
  113. private DatagramChannel openNonBlockingChannel() {
  114. try {
  115. final DatagramChannel channel = DatagramChannel.open();
  116. channel.configureBlocking(false);
  117. return channel;
  118. } catch (final IOException e) {
  119. throw new ChannelException("Failed to open a DatagramChannel.", e);
  120. }
  121. }
  122. @Override
  123. public InetSocketAddress getLocalAddress() {
  124. InetSocketAddress localAddress = this.localAddress;
  125. if (localAddress == null) {
  126. try {
  127. this.localAddress = localAddress =
  128. (InetSocketAddress) datagramChannel.socket().getLocalSocketAddress();
  129. } catch (Throwable t) {
  130. // Sometimes fails on a closed socket in Windows.
  131. return null;
  132. }
  133. }
  134. return localAddress;
  135. }
  136. @Override
  137. public InetSocketAddress getRemoteAddress() {
  138. InetSocketAddress remoteAddress = this.remoteAddress;
  139. if (remoteAddress == null) {
  140. try {
  141. this.remoteAddress = remoteAddress =
  142. (InetSocketAddress) datagramChannel.socket().getRemoteSocketAddress();
  143. } catch (Throwable t) {
  144. // Sometimes fails on a closed socket in Windows.
  145. return null;
  146. }
  147. }
  148. return remoteAddress;
  149. }
  150. @Override
  151. public boolean isBound() {
  152. return isOpen() && datagramChannel.socket().isBound();
  153. }
  154. @Override
  155. public boolean isConnected() {
  156. return datagramChannel.isConnected();
  157. }
  158. @Override
  159. protected boolean setClosed() {
  160. return super.setClosed();
  161. }
  162. @Override
  163. public NioDatagramChannelConfig getConfig() {
  164. return config;
  165. }
  166. DatagramChannel getDatagramChannel() {
  167. return datagramChannel;
  168. }
  169. @Override
  170. public int getInterestOps() {
  171. if (!isOpen()) {
  172. return Channel.OP_WRITE;
  173. }
  174. int interestOps = getRawInterestOps();
  175. int writeBufferSize = this.writeBufferSize.get();
  176. if (writeBufferSize != 0) {
  177. if (highWaterMarkCounter.get() > 0) {
  178. int lowWaterMark = getConfig().getWriteBufferLowWaterMark();
  179. if (writeBufferSize >= lowWaterMark) {
  180. interestOps |= Channel.OP_WRITE;
  181. } else {
  182. interestOps &= ~Channel.OP_WRITE;
  183. }
  184. } else {
  185. int highWaterMark = getConfig().getWriteBufferHighWaterMark();
  186. if (writeBufferSize >= highWaterMark) {
  187. interestOps |= Channel.OP_WRITE;
  188. } else {
  189. interestOps &= ~Channel.OP_WRITE;
  190. }
  191. }
  192. } else {
  193. interestOps &= ~Channel.OP_WRITE;
  194. }
  195. return interestOps;
  196. }
  197. int getRawInterestOps() {
  198. return super.getInterestOps();
  199. }
  200. void setRawInterestOpsNow(int interestOps) {
  201. super.setInterestOpsNow(interestOps);
  202. }
  203. @Override
  204. public ChannelFuture write(Object message, SocketAddress remoteAddress) {
  205. if (remoteAddress == null || remoteAddress.equals(getRemoteAddress())) {
  206. return super.write(message, null);
  207. } else {
  208. return super.write(message, remoteAddress);
  209. }
  210. }
  211. /**
  212. * {@link WriteRequestQueue} is an extension of {@link LinkedTransferQueue}
  213. * that adds support for highWaterMark checking of the write buffer size.
  214. */
  215. private final class WriteRequestQueue extends
  216. LinkedTransferQueue<MessageEvent> {
  217. private static final long serialVersionUID = 5057413071460766376L;
  218. private final ThreadLocalBoolean notifying = new ThreadLocalBoolean();
  219. WriteRequestQueue() {
  220. super();
  221. }
  222. /**
  223. * This method first delegates to {@link LinkedTransferQueue#offer(Object)} and
  224. * adds support for keeping track of the size of the this write buffer.
  225. */
  226. @Override
  227. public boolean offer(MessageEvent e) {
  228. boolean success = super.offer(e);
  229. assert success;
  230. int messageSize = getMessageSize(e);
  231. int newWriteBufferSize = writeBufferSize.addAndGet(messageSize);
  232. int highWaterMark = getConfig().getWriteBufferHighWaterMark();
  233. if (newWriteBufferSize >= highWaterMark) {
  234. if (newWriteBufferSize - messageSize < highWaterMark) {
  235. highWaterMarkCounter.incrementAndGet();
  236. if (!notifying.get()) {
  237. notifying.set(Boolean.TRUE);
  238. fireChannelInterestChanged(NioDatagramChannel.this);
  239. notifying.set(Boolean.FALSE);
  240. }
  241. }
  242. }
  243. return true;
  244. }
  245. /**
  246. * This method first delegates to {@link LinkedTransferQueue#poll()} and
  247. * adds support for keeping track of the size of the this writebuffers queue.
  248. */
  249. @Override
  250. public MessageEvent poll() {
  251. MessageEvent e = super.poll();
  252. if (e != null) {
  253. int messageSize = getMessageSize(e);
  254. int newWriteBufferSize = writeBufferSize.addAndGet(-messageSize);
  255. int lowWaterMark = getConfig().getWriteBufferLowWaterMark();
  256. if (newWriteBufferSize == 0 || newWriteBufferSize < lowWaterMark) {
  257. if (newWriteBufferSize + messageSize >= lowWaterMark) {
  258. highWaterMarkCounter.decrementAndGet();
  259. if (isBound() && !notifying.get()) {
  260. notifying.set(Boolean.TRUE);
  261. fireChannelInterestChanged(NioDatagramChannel.this);
  262. notifying.set(Boolean.FALSE);
  263. }
  264. }
  265. }
  266. }
  267. return e;
  268. }
  269. private int getMessageSize(MessageEvent e) {
  270. Object m = e.getMessage();
  271. if (m instanceof ChannelBuffer) {
  272. return ((ChannelBuffer) m).readableBytes();
  273. }
  274. return 0;
  275. }
  276. }
  277. /**
  278. * WriteTask is a simple runnable performs writes by delegating the {@link NioDatagramWorker}.
  279. *
  280. */
  281. private final class WriteTask implements Runnable {
  282. WriteTask() {
  283. super();
  284. }
  285. @Override
  286. public void run() {
  287. writeTaskInTaskQueue.set(false);
  288. worker.writeFromTaskLoop(NioDatagramChannel.this);
  289. }
  290. }
  291. @Override
  292. public void joinGroup(InetAddress multicastAddress) {
  293. throw new UnsupportedOperationException();
  294. }
  295. @Override
  296. public void joinGroup(InetSocketAddress multicastAddress,
  297. NetworkInterface networkInterface) {
  298. throw new UnsupportedOperationException();
  299. }
  300. @Override
  301. public void leaveGroup(InetAddress multicastAddress) {
  302. throw new UnsupportedOperationException();
  303. }
  304. @Override
  305. public void leaveGroup(InetSocketAddress multicastAddress,
  306. NetworkInterface networkInterface) {
  307. throw new UnsupportedOperationException();
  308. }
  309. }