blob: 029ab3e11689daa132450eed713045590015cf88 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.nio.ch;
27
28import java.io.FileDescriptor;
29import java.io.IOException;
30import java.net.*;
31import java.nio.ByteBuffer;
32import java.nio.channels.*;
33import java.nio.channels.spi.*;
34import java.lang.ref.SoftReference;
35
36
37/**
38 * An implementation of DatagramChannels.
39 */
40
41class DatagramChannelImpl
42 extends DatagramChannel
43 implements SelChImpl
44{
45
46 // Used to make native read and write calls
47 private static NativeDispatcher nd = new DatagramDispatcher();
48
49 // Our file descriptor
50 FileDescriptor fd = null;
51
52 // fd value needed for dev/poll. This value will remain valid
53 // even after the value in the file descriptor object has been set to -1
54 int fdVal;
55
56 // IDs of native threads doing reads and writes, for signalling
57 private volatile long readerThread = 0;
58 private volatile long writerThread = 0;
59
60 // Cached InetAddress and port for unconnected DatagramChannels
61 // used by receive0
62 private InetAddress cachedSenderInetAddress = null;
63 private int cachedSenderPort = 0;
64
65 // Lock held by current reading or connecting thread
66 private final Object readLock = new Object();
67
68 // Lock held by current writing or connecting thread
69 private final Object writeLock = new Object();
70
71 // Lock held by any thread that modifies the state fields declared below
72 // DO NOT invoke a blocking I/O operation while holding this lock!
73 private final Object stateLock = new Object();
74
75 // -- The following fields are protected by stateLock
76
77 // State (does not necessarily increase monotonically)
78 private static final int ST_UNINITIALIZED = -1;
79 private static int ST_UNCONNECTED = 0;
80 private static int ST_CONNECTED = 1;
81 private static final int ST_KILLED = 2;
82 private int state = ST_UNINITIALIZED;
83
84 // Binding
85 private SocketAddress localAddress = null;
86 SocketAddress remoteAddress = null;
87
88 // Options
89 private SocketOpts.IP options = null;
90
91 // Our socket adaptor, if any
92 private DatagramSocket socket = null;
93
94 // -- End of fields protected by stateLock
95
96
97 public DatagramChannelImpl(SelectorProvider sp)
98 throws IOException
99 {
100 super(sp);
101 this.fd = Net.socket(false);
102 this.fdVal = IOUtil.fdVal(fd);
103 this.state = ST_UNCONNECTED;
104 }
105
106 public DatagramChannelImpl(SelectorProvider sp, FileDescriptor fd)
107 throws IOException
108 {
109 super(sp);
110 this.fd = fd;
111 this.fdVal = IOUtil.fdVal(fd);
112 this.state = ST_UNCONNECTED;
113 }
114
115 public DatagramSocket socket() {
116 synchronized (stateLock) {
117 if (socket == null)
118 socket = DatagramSocketAdaptor.create(this);
119 return socket;
120 }
121 }
122
123 private void ensureOpen() throws ClosedChannelException {
124 if (!isOpen())
125 throw new ClosedChannelException();
126 }
127
128 private SocketAddress sender; // Set by receive0 (## ugh)
129
130 public SocketAddress receive(ByteBuffer dst) throws IOException {
131 if (dst.isReadOnly())
132 throw new IllegalArgumentException("Read-only buffer");
133 if (dst == null)
134 throw new NullPointerException();
135 synchronized (readLock) {
136 ensureOpen();
137 // If socket is not bound then behave as if nothing received
138 if (!isBound()) // ## NotYetBoundException ??
139 return null;
140 int n = 0;
141 ByteBuffer bb = null;
142 try {
143 begin();
144 if (!isOpen())
145 return null;
146 SecurityManager security = System.getSecurityManager();
147 readerThread = NativeThread.current();
148 if (isConnected() || (security == null)) {
149 do {
150 n = receive(fd, dst);
151 } while ((n == IOStatus.INTERRUPTED) && isOpen());
152 if (n == IOStatus.UNAVAILABLE)
153 return null;
154 } else {
155 bb = Util.getTemporaryDirectBuffer(dst.remaining());
156 for (;;) {
157 do {
158 n = receive(fd, bb);
159 } while ((n == IOStatus.INTERRUPTED) && isOpen());
160 if (n == IOStatus.UNAVAILABLE)
161 return null;
162 InetSocketAddress isa = (InetSocketAddress)sender;
163 try {
164 security.checkAccept(
165 isa.getAddress().getHostAddress(),
166 isa.getPort());
167 } catch (SecurityException se) {
168 // Ignore packet
169 bb.clear();
170 n = 0;
171 continue;
172 }
173 bb.flip();
174 dst.put(bb);
175 break;
176 }
177 }
178 return sender;
179 } finally {
180 if (bb != null)
181 Util.releaseTemporaryDirectBuffer(bb);
182 readerThread = 0;
183 end((n > 0) || (n == IOStatus.UNAVAILABLE));
184 assert IOStatus.check(n);
185 }
186 }
187 }
188
189 private int receive(FileDescriptor fd, ByteBuffer dst)
190 throws IOException
191 {
192 int pos = dst.position();
193 int lim = dst.limit();
194 assert (pos <= lim);
195 int rem = (pos <= lim ? lim - pos : 0);
196 if (dst instanceof DirectBuffer && rem > 0)
197 return receiveIntoNativeBuffer(fd, dst, rem, pos);
198
199 // Substitute a native buffer. If the supplied buffer is empty
200 // we must instead use a nonempty buffer, otherwise the call
201 // will not block waiting for a datagram on some platforms.
202 int newSize = Math.max(rem, 1);
203 ByteBuffer bb = null;
204 try {
205 bb = Util.getTemporaryDirectBuffer(newSize);
206 int n = receiveIntoNativeBuffer(fd, bb, newSize, 0);
207 bb.flip();
208 if (n > 0 && rem > 0)
209 dst.put(bb);
210 return n;
211 } finally {
212 Util.releaseTemporaryDirectBuffer(bb);
213 }
214 }
215
216 private int receiveIntoNativeBuffer(FileDescriptor fd, ByteBuffer bb,
217 int rem, int pos)
218 throws IOException
219 {
220 int n = receive0(fd, ((DirectBuffer)bb).address() + pos, rem,
221 isConnected());
222 if (n > 0)
223 bb.position(pos + n);
224 return n;
225 }
226
227 public int send(ByteBuffer src, SocketAddress target)
228 throws IOException
229 {
230 if (src == null)
231 throw new NullPointerException();
232
233 synchronized (writeLock) {
234 ensureOpen();
235 InetSocketAddress isa = (InetSocketAddress)target;
236 InetAddress ia = isa.getAddress();
237 if (ia == null)
238 throw new IOException("Target address not resolved");
239 synchronized (stateLock) {
240 if (!isConnected()) {
241 if (target == null)
242 throw new NullPointerException();
243 SecurityManager sm = System.getSecurityManager();
244 if (sm != null) {
245 if (ia.isMulticastAddress()) {
246 sm.checkMulticast(isa.getAddress());
247 } else {
248 sm.checkConnect(isa.getAddress().getHostAddress(),
249 isa.getPort());
250 }
251 }
252 } else { // Connected case; Check address then write
253 if (!target.equals(remoteAddress)) {
254 throw new IllegalArgumentException(
255 "Connected address not equal to target address");
256 }
257 return write(src);
258 }
259 }
260
261 int n = 0;
262 try {
263 begin();
264 if (!isOpen())
265 return 0;
266 writerThread = NativeThread.current();
267 do {
268 n = send(fd, src, target);
269 } while ((n == IOStatus.INTERRUPTED) && isOpen());
270 return IOStatus.normalize(n);
271 } finally {
272 writerThread = 0;
273 end((n > 0) || (n == IOStatus.UNAVAILABLE));
274 assert IOStatus.check(n);
275 }
276 }
277 }
278
279 private int send(FileDescriptor fd, ByteBuffer src, SocketAddress target)
280 throws IOException
281 {
282 if (src instanceof DirectBuffer)
283 return sendFromNativeBuffer(fd, src, target);
284
285 // Substitute a native buffer
286 int pos = src.position();
287 int lim = src.limit();
288 assert (pos <= lim);
289 int rem = (pos <= lim ? lim - pos : 0);
290
291 ByteBuffer bb = null;
292 try {
293 bb = Util.getTemporaryDirectBuffer(rem);
294 bb.put(src);
295 bb.flip();
296 // Do not update src until we see how many bytes were written
297 src.position(pos);
298
299 int n = sendFromNativeBuffer(fd, bb, target);
300 if (n > 0) {
301 // now update src
302 src.position(pos + n);
303 }
304 return n;
305 } finally {
306 Util.releaseTemporaryDirectBuffer(bb);
307 }
308 }
309
310 private int sendFromNativeBuffer(FileDescriptor fd, ByteBuffer bb,
311 SocketAddress target)
312 throws IOException
313 {
314 int pos = bb.position();
315 int lim = bb.limit();
316 assert (pos <= lim);
317 int rem = (pos <= lim ? lim - pos : 0);
318
319 int written = send0(fd, ((DirectBuffer)bb).address() + pos,
320 rem, target);
321 if (written > 0)
322 bb.position(pos + written);
323 return written;
324 }
325
326 public int read(ByteBuffer buf) throws IOException {
327 if (buf == null)
328 throw new NullPointerException();
329 synchronized (readLock) {
330 synchronized (stateLock) {
331 ensureOpen();
332 if (!isConnected())
333 throw new NotYetConnectedException();
334 }
335 int n = 0;
336 try {
337 begin();
338 if (!isOpen())
339 return 0;
340 readerThread = NativeThread.current();
341 do {
342 n = IOUtil.read(fd, buf, -1, nd, readLock);
343 } while ((n == IOStatus.INTERRUPTED) && isOpen());
344 return IOStatus.normalize(n);
345 } finally {
346 readerThread = 0;
347 end((n > 0) || (n == IOStatus.UNAVAILABLE));
348 assert IOStatus.check(n);
349 }
350 }
351 }
352
353 private long read0(ByteBuffer[] bufs) throws IOException {
354 if (bufs == null)
355 throw new NullPointerException();
356 synchronized (readLock) {
357 synchronized (stateLock) {
358 ensureOpen();
359 if (!isConnected())
360 throw new NotYetConnectedException();
361 }
362 long n = 0;
363 try {
364 begin();
365 if (!isOpen())
366 return 0;
367 readerThread = NativeThread.current();
368 do {
369 n = IOUtil.read(fd, bufs, nd);
370 } while ((n == IOStatus.INTERRUPTED) && isOpen());
371 return IOStatus.normalize(n);
372 } finally {
373 readerThread = 0;
374 end((n > 0) || (n == IOStatus.UNAVAILABLE));
375 assert IOStatus.check(n);
376 }
377 }
378 }
379
380 public long read(ByteBuffer[] dsts, int offset, int length)
381 throws IOException
382 {
383 if ((offset < 0) || (length < 0) || (offset > dsts.length - length))
384 throw new IndexOutOfBoundsException();
385 // ## Fix IOUtil.write so that we can avoid this array copy
386 return read0(Util.subsequence(dsts, offset, length));
387 }
388
389 public int write(ByteBuffer buf) throws IOException {
390 if (buf == null)
391 throw new NullPointerException();
392 synchronized (writeLock) {
393 synchronized (stateLock) {
394 ensureOpen();
395 if (!isConnected())
396 throw new NotYetConnectedException();
397 }
398 int n = 0;
399 try {
400 begin();
401 if (!isOpen())
402 return 0;
403 writerThread = NativeThread.current();
404 do {
405 n = IOUtil.write(fd, buf, -1, nd, writeLock);
406 } while ((n == IOStatus.INTERRUPTED) && isOpen());
407 return IOStatus.normalize(n);
408 } finally {
409 writerThread = 0;
410 end((n > 0) || (n == IOStatus.UNAVAILABLE));
411 assert IOStatus.check(n);
412 }
413 }
414 }
415
416 private long write0(ByteBuffer[] bufs) throws IOException {
417 if (bufs == null)
418 throw new NullPointerException();
419 synchronized (writeLock) {
420 synchronized (stateLock) {
421 ensureOpen();
422 if (!isConnected())
423 throw new NotYetConnectedException();
424 }
425 long n = 0;
426 try {
427 begin();
428 if (!isOpen())
429 return 0;
430 writerThread = NativeThread.current();
431 do {
432 n = IOUtil.write(fd, bufs, nd);
433 } while ((n == IOStatus.INTERRUPTED) && isOpen());
434 return IOStatus.normalize(n);
435 } finally {
436 writerThread = 0;
437 end((n > 0) || (n == IOStatus.UNAVAILABLE));
438 assert IOStatus.check(n);
439 }
440 }
441 }
442
443 public long write(ByteBuffer[] srcs, int offset, int length)
444 throws IOException
445 {
446 if ((offset < 0) || (length < 0) || (offset > srcs.length - length))
447 throw new IndexOutOfBoundsException();
448 // ## Fix IOUtil.write so that we can avoid this array copy
449 return write0(Util.subsequence(srcs, offset, length));
450 }
451
452 protected void implConfigureBlocking(boolean block) throws IOException {
453 IOUtil.configureBlocking(fd, block);
454 }
455
456 public SocketOpts options() {
457 synchronized (stateLock) {
458 if (options == null) {
459 SocketOptsImpl.Dispatcher d
460 = new SocketOptsImpl.Dispatcher() {
461 int getInt(int opt) throws IOException {
462 return Net.getIntOption(fd, opt);
463 }
464 void setInt(int opt, int arg)
465 throws IOException
466 {
467 Net.setIntOption(fd, opt, arg);
468 }
469 };
470 options = new SocketOptsImpl.IP(d);
471 }
472 return options;
473 }
474 }
475
476 public boolean isBound() {
477 return Net.localPortNumber(fd) != 0;
478 }
479
480 public SocketAddress localAddress() {
481 synchronized (stateLock) {
482 if (isConnected() && (localAddress == null)) {
483 // Socket was not bound before connecting,
484 // so ask what the address turned out to be
485 localAddress = Net.localAddress(fd);
486 }
487 SecurityManager sm = System.getSecurityManager();
488 if (sm != null) {
489 InetSocketAddress isa = (InetSocketAddress)localAddress;
490 sm.checkConnect(isa.getAddress().getHostAddress(), -1);
491 }
492 return localAddress;
493 }
494 }
495
496 public SocketAddress remoteAddress() {
497 synchronized (stateLock) {
498 return remoteAddress;
499 }
500 }
501
502 public void bind(SocketAddress local) throws IOException {
503 synchronized (readLock) {
504 synchronized (writeLock) {
505 synchronized (stateLock) {
506 ensureOpen();
507 if (isBound())
508 throw new AlreadyBoundException();
509 InetSocketAddress isa = Net.checkAddress(local);
510 SecurityManager sm = System.getSecurityManager();
511 if (sm != null)
512 sm.checkListen(isa.getPort());
513 Net.bind(fd, isa.getAddress(), isa.getPort());
514 localAddress = Net.localAddress(fd);
515 }
516 }
517 }
518 }
519
520 public boolean isConnected() {
521 synchronized (stateLock) {
522 return (state == ST_CONNECTED);
523 }
524 }
525
526 void ensureOpenAndUnconnected() throws IOException { // package-private
527 synchronized (stateLock) {
528 if (!isOpen())
529 throw new ClosedChannelException();
530 if (state != ST_UNCONNECTED)
531 throw new IllegalStateException("Connect already invoked");
532 }
533 }
534
535 public DatagramChannel connect(SocketAddress sa) throws IOException {
536 int trafficClass = 0;
537 int localPort = 0;
538
539 synchronized(readLock) {
540 synchronized(writeLock) {
541 synchronized (stateLock) {
542 ensureOpenAndUnconnected();
543 InetSocketAddress isa = Net.checkAddress(sa);
544 SecurityManager sm = System.getSecurityManager();
545 if (sm != null)
546 sm.checkConnect(isa.getAddress().getHostAddress(),
547 isa.getPort());
548 int n = Net.connect(fd,
549 isa.getAddress(),
550 isa.getPort(),
551 trafficClass);
552 if (n <= 0)
553 throw new Error(); // Can't happen
554
555 // Connection succeeded; disallow further invocation
556 state = ST_CONNECTED;
557 remoteAddress = sa;
558 sender = isa;
559 cachedSenderInetAddress = isa.getAddress();
560 cachedSenderPort = isa.getPort();
561 }
562 }
563 }
564 return this;
565 }
566
567 public DatagramChannel disconnect() throws IOException {
568 synchronized(readLock) {
569 synchronized(writeLock) {
570 synchronized (stateLock) {
571 if (!isConnected() || !isOpen())
572 return this;
573 InetSocketAddress isa = (InetSocketAddress)remoteAddress;
574 SecurityManager sm = System.getSecurityManager();
575 if (sm != null)
576 sm.checkConnect(isa.getAddress().getHostAddress(),
577 isa.getPort());
578 disconnect0(fd);
579 remoteAddress = null;
580 state = ST_UNCONNECTED;
581 }
582 }
583 }
584 return this;
585 }
586
587 protected void implCloseSelectableChannel() throws IOException {
588 synchronized (stateLock) {
589 nd.preClose(fd);
590 long th;
591 if ((th = readerThread) != 0)
592 NativeThread.signal(th);
593 if ((th = writerThread) != 0)
594 NativeThread.signal(th);
595 if (!isRegistered())
596 kill();
597 }
598 }
599
600 public void kill() throws IOException {
601 synchronized (stateLock) {
602 if (state == ST_KILLED)
603 return;
604 if (state == ST_UNINITIALIZED) {
605 state = ST_KILLED;
606 return;
607 }
608 assert !isOpen() && !isRegistered();
609 nd.close(fd);
610 state = ST_KILLED;
611 }
612 }
613
614 protected void finalize() throws IOException {
615 // fd is null if constructor threw exception
616 if (fd != null)
617 close();
618 }
619
620 /**
621 * Translates native poll revent set into a ready operation set
622 */
623 public boolean translateReadyOps(int ops, int initialOps,
624 SelectionKeyImpl sk) {
625 int intOps = sk.nioInterestOps(); // Do this just once, it synchronizes
626 int oldOps = sk.nioReadyOps();
627 int newOps = initialOps;
628
629 if ((ops & PollArrayWrapper.POLLNVAL) != 0) {
630 // This should only happen if this channel is pre-closed while a
631 // selection operation is in progress
632 // ## Throw an error if this channel has not been pre-closed
633 return false;
634 }
635
636 if ((ops & (PollArrayWrapper.POLLERR
637 | PollArrayWrapper.POLLHUP)) != 0) {
638 newOps = intOps;
639 sk.nioReadyOps(newOps);
640 return (newOps & ~oldOps) != 0;
641 }
642
643 if (((ops & PollArrayWrapper.POLLIN) != 0) &&
644 ((intOps & SelectionKey.OP_READ) != 0))
645 newOps |= SelectionKey.OP_READ;
646
647 if (((ops & PollArrayWrapper.POLLOUT) != 0) &&
648 ((intOps & SelectionKey.OP_WRITE) != 0))
649 newOps |= SelectionKey.OP_WRITE;
650
651 sk.nioReadyOps(newOps);
652 return (newOps & ~oldOps) != 0;
653 }
654
655 public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) {
656 return translateReadyOps(ops, sk.nioReadyOps(), sk);
657 }
658
659 public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) {
660 return translateReadyOps(ops, 0, sk);
661 }
662
663 /**
664 * Translates an interest operation set into a native poll event set
665 */
666 public void translateAndSetInterestOps(int ops, SelectionKeyImpl sk) {
667 int newOps = 0;
668
669 if ((ops & SelectionKey.OP_READ) != 0)
670 newOps |= PollArrayWrapper.POLLIN;
671 if ((ops & SelectionKey.OP_WRITE) != 0)
672 newOps |= PollArrayWrapper.POLLOUT;
673 if ((ops & SelectionKey.OP_CONNECT) != 0)
674 newOps |= PollArrayWrapper.POLLIN;
675 sk.selector.putEventOps(sk, newOps);
676 }
677
678 public FileDescriptor getFD() {
679 return fd;
680 }
681
682 public int getFDVal() {
683 return fdVal;
684 }
685
686
687 // -- Native methods --
688
689 private static native void initIDs();
690
691 private static native void disconnect0(FileDescriptor fd)
692 throws IOException;
693
694 private native int receive0(FileDescriptor fd, long address, int len,
695 boolean connected)
696 throws IOException;
697
698 private native int send0(FileDescriptor fd, long address, int len,
699 SocketAddress sa)
700 throws IOException;
701
702 static {
703 Util.load();
704 initIDs();
705 }
706
707}