45 |
45 |
46 // Used to make native read and write calls |
46 // Used to make native read and write calls |
47 private static NativeDispatcher nd = new DatagramDispatcher(); |
47 private static NativeDispatcher nd = new DatagramDispatcher(); |
48 |
48 |
49 // Our file descriptor |
49 // Our file descriptor |
50 FileDescriptor fd = null; |
50 private final FileDescriptor fd; |
51 |
51 |
52 // fd value needed for dev/poll. This value will remain valid |
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 |
53 // even after the value in the file descriptor object has been set to -1 |
54 int fdVal; |
54 private final int fdVal; |
|
55 |
|
56 // The protocol family of the socket |
|
57 private final ProtocolFamily family; |
55 |
58 |
56 // IDs of native threads doing reads and writes, for signalling |
59 // IDs of native threads doing reads and writes, for signalling |
57 private volatile long readerThread = 0; |
60 private volatile long readerThread = 0; |
58 private volatile long writerThread = 0; |
61 private volatile long writerThread = 0; |
59 |
62 |
60 // Cached InetAddress and port for unconnected DatagramChannels |
63 // Cached InetAddress and port for unconnected DatagramChannels |
61 // used by receive0 |
64 // used by receive0 |
62 private InetAddress cachedSenderInetAddress = null; |
65 private InetAddress cachedSenderInetAddress; |
63 private int cachedSenderPort = 0; |
66 private int cachedSenderPort; |
64 |
67 |
65 // Lock held by current reading or connecting thread |
68 // Lock held by current reading or connecting thread |
66 private final Object readLock = new Object(); |
69 private final Object readLock = new Object(); |
67 |
70 |
68 // Lock held by current writing or connecting thread |
71 // Lock held by current writing or connecting thread |
74 |
77 |
75 // -- The following fields are protected by stateLock |
78 // -- The following fields are protected by stateLock |
76 |
79 |
77 // State (does not necessarily increase monotonically) |
80 // State (does not necessarily increase monotonically) |
78 private static final int ST_UNINITIALIZED = -1; |
81 private static final int ST_UNINITIALIZED = -1; |
79 private static int ST_UNCONNECTED = 0; |
82 private static final int ST_UNCONNECTED = 0; |
80 private static int ST_CONNECTED = 1; |
83 private static final int ST_CONNECTED = 1; |
81 private static final int ST_KILLED = 2; |
84 private static final int ST_KILLED = 2; |
82 private int state = ST_UNINITIALIZED; |
85 private int state = ST_UNINITIALIZED; |
83 |
86 |
84 // Binding |
87 // Binding |
85 private SocketAddress localAddress = null; |
88 private SocketAddress localAddress; |
86 SocketAddress remoteAddress = null; |
89 private SocketAddress remoteAddress; |
87 |
|
88 // Options |
|
89 private SocketOpts.IP options = null; |
|
90 |
90 |
91 // Our socket adaptor, if any |
91 // Our socket adaptor, if any |
92 private DatagramSocket socket = null; |
92 private DatagramSocket socket; |
|
93 |
|
94 // Multicast support |
|
95 private MembershipRegistry registry; |
93 |
96 |
94 // -- End of fields protected by stateLock |
97 // -- End of fields protected by stateLock |
95 |
98 |
96 |
99 |
97 public DatagramChannelImpl(SelectorProvider sp) |
100 public DatagramChannelImpl(SelectorProvider sp) |
98 throws IOException |
101 throws IOException |
99 { |
102 { |
100 super(sp); |
103 super(sp); |
101 this.fd = Net.socket(false); |
104 this.family = Net.isIPv6Available() ? |
|
105 StandardProtocolFamily.INET6 : StandardProtocolFamily.INET; |
|
106 this.fd = Net.socket(family, false); |
102 this.fdVal = IOUtil.fdVal(fd); |
107 this.fdVal = IOUtil.fdVal(fd); |
103 this.state = ST_UNCONNECTED; |
108 this.state = ST_UNCONNECTED; |
104 } |
109 } |
105 |
110 |
|
111 public DatagramChannelImpl(SelectorProvider sp, ProtocolFamily family) { |
|
112 super(sp); |
|
113 if ((family != StandardProtocolFamily.INET) && |
|
114 (family != StandardProtocolFamily.INET6)) { |
|
115 throw new UnsupportedOperationException("Protocol family not supported"); |
|
116 } |
|
117 if (family == StandardProtocolFamily.INET6) { |
|
118 if (!Net.isIPv6Available()) { |
|
119 throw new UnsupportedOperationException("IPv6 not available"); |
|
120 } |
|
121 } |
|
122 this.family = family; |
|
123 this.fd = Net.socket(family, false); |
|
124 this.fdVal = IOUtil.fdVal(fd); |
|
125 this.state = ST_UNCONNECTED; |
|
126 } |
|
127 |
106 public DatagramChannelImpl(SelectorProvider sp, FileDescriptor fd) |
128 public DatagramChannelImpl(SelectorProvider sp, FileDescriptor fd) |
107 throws IOException |
129 throws IOException |
108 { |
130 { |
109 super(sp); |
131 super(sp); |
|
132 this.family = Net.isIPv6Available() ? |
|
133 StandardProtocolFamily.INET6 : StandardProtocolFamily.INET; |
110 this.fd = fd; |
134 this.fd = fd; |
111 this.fdVal = IOUtil.fdVal(fd); |
135 this.fdVal = IOUtil.fdVal(fd); |
112 this.state = ST_UNCONNECTED; |
136 this.state = ST_UNCONNECTED; |
|
137 this.localAddress = Net.localAddress(fd); |
113 } |
138 } |
114 |
139 |
115 public DatagramSocket socket() { |
140 public DatagramSocket socket() { |
116 synchronized (stateLock) { |
141 synchronized (stateLock) { |
117 if (socket == null) |
142 if (socket == null) |
118 socket = DatagramSocketAdaptor.create(this); |
143 socket = DatagramSocketAdaptor.create(this); |
119 return socket; |
144 return socket; |
120 } |
145 } |
|
146 } |
|
147 |
|
148 @Override |
|
149 public SocketAddress getLocalAddress() throws IOException { |
|
150 synchronized (stateLock) { |
|
151 if (!isOpen()) |
|
152 return null; |
|
153 return localAddress; |
|
154 } |
|
155 } |
|
156 |
|
157 @Override |
|
158 public SocketAddress getConnectedAddress() throws IOException { |
|
159 synchronized (stateLock) { |
|
160 if (!isOpen()) |
|
161 return null; |
|
162 return remoteAddress; |
|
163 } |
|
164 } |
|
165 |
|
166 @Override |
|
167 public DatagramChannel setOption(SocketOption name, Object value) |
|
168 throws IOException |
|
169 { |
|
170 if (name == null) |
|
171 throw new NullPointerException(); |
|
172 if (!options().contains(name)) |
|
173 throw new IllegalArgumentException("Invalid option name"); |
|
174 |
|
175 synchronized (stateLock) { |
|
176 ensureOpen(); |
|
177 |
|
178 if (name == StandardSocketOption.IP_TOS) { |
|
179 // IPv4 only; no-op for IPv6 |
|
180 if (family == StandardProtocolFamily.INET) { |
|
181 Net.setSocketOption(fd, family, name, value); |
|
182 } |
|
183 return this; |
|
184 } |
|
185 |
|
186 if (name == StandardSocketOption.IP_MULTICAST_TTL || |
|
187 name == StandardSocketOption.IP_MULTICAST_LOOP) |
|
188 { |
|
189 // options are protocol dependent |
|
190 Net.setSocketOption(fd, family, name, value); |
|
191 return this; |
|
192 } |
|
193 |
|
194 if (name == StandardSocketOption.IP_MULTICAST_IF) { |
|
195 if (value == null) |
|
196 throw new IllegalArgumentException("Cannot set IP_MULTICAST_IF to 'null'"); |
|
197 NetworkInterface interf = (NetworkInterface)value; |
|
198 if (family == StandardProtocolFamily.INET6) { |
|
199 int index = interf.getIndex(); |
|
200 if (index == -1) |
|
201 throw new IOException("Network interface cannot be identified"); |
|
202 Net.setInterface6(fd, index); |
|
203 } else { |
|
204 // need IPv4 address to identify interface |
|
205 Inet4Address target = Net.anyInet4Address(interf); |
|
206 if (target == null) |
|
207 throw new IOException("Network interface not configured for IPv4"); |
|
208 int targetAddress = Net.inet4AsInt(target); |
|
209 Net.setInterface4(fd, targetAddress); |
|
210 } |
|
211 return this; |
|
212 } |
|
213 |
|
214 // remaining options don't need any special handling |
|
215 Net.setSocketOption(fd, Net.UNSPEC, name, value); |
|
216 return this; |
|
217 } |
|
218 } |
|
219 |
|
220 @Override |
|
221 @SuppressWarnings("unchecked") |
|
222 public <T> T getOption(SocketOption<T> name) |
|
223 throws IOException |
|
224 { |
|
225 if (name == null) |
|
226 throw new NullPointerException(); |
|
227 if (!options().contains(name)) |
|
228 throw new IllegalArgumentException("Invalid option name"); |
|
229 |
|
230 synchronized (stateLock) { |
|
231 ensureOpen(); |
|
232 |
|
233 if (name == StandardSocketOption.IP_TOS) { |
|
234 // IPv4 only; always return 0 on IPv6 |
|
235 if (family == StandardProtocolFamily.INET) { |
|
236 return (T) Net.getSocketOption(fd, family, name); |
|
237 } else { |
|
238 return (T) Integer.valueOf(0); |
|
239 } |
|
240 } |
|
241 |
|
242 if (name == StandardSocketOption.IP_MULTICAST_TTL || |
|
243 name == StandardSocketOption.IP_MULTICAST_LOOP) |
|
244 { |
|
245 return (T) Net.getSocketOption(fd, family, name); |
|
246 } |
|
247 |
|
248 if (name == StandardSocketOption.IP_MULTICAST_IF) { |
|
249 if (family == StandardProtocolFamily.INET) { |
|
250 int address = Net.getInterface4(fd); |
|
251 if (address == 0) |
|
252 return null; // default interface |
|
253 |
|
254 InetAddress ia = Net.inet4FromInt(address); |
|
255 NetworkInterface ni = NetworkInterface.getByInetAddress(ia); |
|
256 if (ni == null) |
|
257 throw new IOException("Unable to map address to interface"); |
|
258 return (T) ni; |
|
259 } else { |
|
260 int index = Net.getInterface6(fd); |
|
261 if (index == 0) |
|
262 return null; // default interface |
|
263 |
|
264 NetworkInterface ni = NetworkInterface.getByIndex(index); |
|
265 if (ni == null) |
|
266 throw new IOException("Unable to map index to interface"); |
|
267 return (T) ni; |
|
268 } |
|
269 } |
|
270 |
|
271 // no special handling |
|
272 return (T) Net.getSocketOption(fd, Net.UNSPEC, name); |
|
273 } |
|
274 } |
|
275 |
|
276 private static class LazyInitialization { |
|
277 static final Set<SocketOption<?>> defaultOptions = defaultOptions(); |
|
278 |
|
279 private static Set<SocketOption<?>> defaultOptions() { |
|
280 HashSet<SocketOption<?>> set = new HashSet<SocketOption<?>>(8); |
|
281 set.add(StandardSocketOption.SO_SNDBUF); |
|
282 set.add(StandardSocketOption.SO_RCVBUF); |
|
283 set.add(StandardSocketOption.SO_REUSEADDR); |
|
284 set.add(StandardSocketOption.SO_BROADCAST); |
|
285 set.add(StandardSocketOption.IP_TOS); |
|
286 set.add(StandardSocketOption.IP_MULTICAST_IF); |
|
287 set.add(StandardSocketOption.IP_MULTICAST_TTL); |
|
288 set.add(StandardSocketOption.IP_MULTICAST_LOOP); |
|
289 return Collections.unmodifiableSet(set); |
|
290 } |
|
291 } |
|
292 |
|
293 @Override |
|
294 public final Set<SocketOption<?>> options() { |
|
295 return LazyInitialization.defaultOptions; |
121 } |
296 } |
122 |
297 |
123 private void ensureOpen() throws ClosedChannelException { |
298 private void ensureOpen() throws ClosedChannelException { |
124 if (!isOpen()) |
299 if (!isOpen()) |
125 throw new ClosedChannelException(); |
300 throw new ClosedChannelException(); |
451 |
635 |
452 protected void implConfigureBlocking(boolean block) throws IOException { |
636 protected void implConfigureBlocking(boolean block) throws IOException { |
453 IOUtil.configureBlocking(fd, block); |
637 IOUtil.configureBlocking(fd, block); |
454 } |
638 } |
455 |
639 |
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() { |
640 public SocketAddress localAddress() { |
481 synchronized (stateLock) { |
641 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; |
642 return localAddress; |
493 } |
643 } |
494 } |
644 } |
495 |
645 |
496 public SocketAddress remoteAddress() { |
646 public SocketAddress remoteAddress() { |
497 synchronized (stateLock) { |
647 synchronized (stateLock) { |
498 return remoteAddress; |
648 return remoteAddress; |
499 } |
649 } |
500 } |
650 } |
501 |
651 |
502 public void bind(SocketAddress local) throws IOException { |
652 @Override |
|
653 public DatagramChannel bind(SocketAddress local) throws IOException { |
503 synchronized (readLock) { |
654 synchronized (readLock) { |
504 synchronized (writeLock) { |
655 synchronized (writeLock) { |
505 synchronized (stateLock) { |
656 synchronized (stateLock) { |
506 ensureOpen(); |
657 ensureOpen(); |
507 if (isBound()) |
658 if (localAddress != null) |
508 throw new AlreadyBoundException(); |
659 throw new AlreadyBoundException(); |
509 InetSocketAddress isa = Net.checkAddress(local); |
660 InetSocketAddress isa; |
|
661 if (local == null) { |
|
662 isa = new InetSocketAddress(0); |
|
663 } else { |
|
664 isa = Net.checkAddress(local); |
|
665 |
|
666 // only Inet4Address allowed with IPv4 socket |
|
667 if (family == StandardProtocolFamily.INET) { |
|
668 InetAddress addr = isa.getAddress(); |
|
669 if (!(addr instanceof Inet4Address)) |
|
670 throw new UnsupportedAddressTypeException(); |
|
671 } |
|
672 } |
510 SecurityManager sm = System.getSecurityManager(); |
673 SecurityManager sm = System.getSecurityManager(); |
511 if (sm != null) |
674 if (sm != null) { |
512 sm.checkListen(isa.getPort()); |
675 sm.checkListen(isa.getPort()); |
513 Net.bind(fd, isa.getAddress(), isa.getPort()); |
676 } |
|
677 Net.bind(family, fd, isa.getAddress(), isa.getPort()); |
514 localAddress = Net.localAddress(fd); |
678 localAddress = Net.localAddress(fd); |
515 } |
679 } |
516 } |
680 } |
517 } |
681 } |
|
682 return this; |
518 } |
683 } |
519 |
684 |
520 public boolean isConnected() { |
685 public boolean isConnected() { |
521 synchronized (stateLock) { |
686 synchronized (stateLock) { |
522 return (state == ST_CONNECTED); |
687 return (state == ST_CONNECTED); |
582 } |
751 } |
583 } |
752 } |
584 return this; |
753 return this; |
585 } |
754 } |
586 |
755 |
|
756 /** |
|
757 * Joins channel's socket to the given group/interface and |
|
758 * optional source address. |
|
759 */ |
|
760 private MembershipKey innerJoin(InetAddress group, |
|
761 NetworkInterface interf, |
|
762 InetAddress source) |
|
763 throws IOException |
|
764 { |
|
765 if (!group.isMulticastAddress()) |
|
766 throw new IllegalArgumentException("Group not a multicast address"); |
|
767 |
|
768 // check multicast address is compatible with this socket |
|
769 if (!(group instanceof Inet4Address)) { |
|
770 if (family == StandardProtocolFamily.INET) |
|
771 throw new IllegalArgumentException("Group is not IPv4 address"); |
|
772 if (!(group instanceof Inet6Address)) |
|
773 throw new IllegalArgumentException("Address type not supported"); |
|
774 } |
|
775 |
|
776 // check source address |
|
777 if (source != null) { |
|
778 if (source.isAnyLocalAddress()) |
|
779 throw new IllegalArgumentException("Source address is a wildcard address"); |
|
780 if (source.isMulticastAddress()) |
|
781 throw new IllegalArgumentException("Source address is multicast address"); |
|
782 if (source.getClass() != group.getClass()) |
|
783 throw new IllegalArgumentException("Source address is different type to group"); |
|
784 } |
|
785 |
|
786 SecurityManager sm = System.getSecurityManager(); |
|
787 if (sm != null) |
|
788 sm.checkMulticast(group); |
|
789 |
|
790 synchronized (stateLock) { |
|
791 if (!isOpen()) |
|
792 throw new ClosedChannelException(); |
|
793 |
|
794 // check the registry to see if we are already a member of the group |
|
795 if (registry == null) { |
|
796 registry = new MembershipRegistry(); |
|
797 } else { |
|
798 // return existing membership key |
|
799 MembershipKey key = registry.checkMembership(group, interf, source); |
|
800 if (key != null) |
|
801 return key; |
|
802 } |
|
803 |
|
804 MembershipKeyImpl key; |
|
805 if (family == StandardProtocolFamily.INET6) { |
|
806 int index = interf.getIndex(); |
|
807 if (index == -1) |
|
808 throw new IOException("Network interface cannot be identified"); |
|
809 |
|
810 // need multicast and source address as byte arrays |
|
811 byte[] groupAddress = Net.inet6AsByteArray(group); |
|
812 byte[] sourceAddress = (source == null) ? null : |
|
813 Net.inet6AsByteArray(source); |
|
814 |
|
815 // join the group |
|
816 int n = Net.join6(fd, groupAddress, index, sourceAddress); |
|
817 if (n == IOStatus.UNAVAILABLE) |
|
818 throw new UnsupportedOperationException(); |
|
819 |
|
820 key = new MembershipKeyImpl.Type6(this, group, interf, source, |
|
821 groupAddress, index, sourceAddress); |
|
822 |
|
823 } else { |
|
824 // need IPv4 address to identify interface |
|
825 Inet4Address target = Net.anyInet4Address(interf); |
|
826 if (target == null) |
|
827 throw new IOException("Network interface not configured for IPv4"); |
|
828 |
|
829 int groupAddress = Net.inet4AsInt(group); |
|
830 int targetAddress = Net.inet4AsInt(target); |
|
831 int sourceAddress = (source == null) ? 0 : Net.inet4AsInt(source); |
|
832 |
|
833 // join the group |
|
834 int n = Net.join4(fd, groupAddress, targetAddress, sourceAddress); |
|
835 if (n == IOStatus.UNAVAILABLE) |
|
836 throw new UnsupportedOperationException(); |
|
837 |
|
838 key = new MembershipKeyImpl.Type4(this, group, interf, source, |
|
839 groupAddress, targetAddress, sourceAddress); |
|
840 } |
|
841 |
|
842 registry.add(key); |
|
843 return key; |
|
844 } |
|
845 } |
|
846 |
|
847 @Override |
|
848 public MembershipKey join(InetAddress group, |
|
849 NetworkInterface interf) |
|
850 throws IOException |
|
851 { |
|
852 return innerJoin(group, interf, null); |
|
853 } |
|
854 |
|
855 @Override |
|
856 public MembershipKey join(InetAddress group, |
|
857 NetworkInterface interf, |
|
858 InetAddress source) |
|
859 throws IOException |
|
860 { |
|
861 if (source == null) |
|
862 throw new NullPointerException("source address is null"); |
|
863 return innerJoin(group, interf, source); |
|
864 } |
|
865 |
|
866 // package-private |
|
867 void drop(MembershipKeyImpl key) |
|
868 throws IOException |
|
869 { |
|
870 assert key.getChannel() == this; |
|
871 |
|
872 synchronized (stateLock) { |
|
873 if (!key.isValid()) |
|
874 return; |
|
875 |
|
876 if (family == StandardProtocolFamily.INET6) { |
|
877 MembershipKeyImpl.Type6 key6 = |
|
878 (MembershipKeyImpl.Type6)key; |
|
879 Net.drop6(fd, key6.group(), key6.index(), key6.source()); |
|
880 } else { |
|
881 MembershipKeyImpl.Type4 key4 = |
|
882 (MembershipKeyImpl.Type4)key; |
|
883 Net.drop4(fd, key4.group(), key4.interfaceAddress(), key4.source()); |
|
884 } |
|
885 |
|
886 key.invalidate(); |
|
887 registry.remove(key); |
|
888 } |
|
889 } |
|
890 |
|
891 /** |
|
892 * Block datagrams from given source if a memory to receive all |
|
893 * datagrams. |
|
894 */ |
|
895 void block(MembershipKeyImpl key, InetAddress source) |
|
896 throws IOException |
|
897 { |
|
898 assert key.getChannel() == this; |
|
899 assert key.getSourceAddress() == null; |
|
900 |
|
901 synchronized (stateLock) { |
|
902 if (!key.isValid()) |
|
903 throw new IllegalStateException("key is no longer valid"); |
|
904 if (source.isAnyLocalAddress()) |
|
905 throw new IllegalArgumentException("Source address is a wildcard address"); |
|
906 if (source.isMulticastAddress()) |
|
907 throw new IllegalArgumentException("Source address is multicast address"); |
|
908 if (source.getClass() != key.getGroup().getClass()) |
|
909 throw new IllegalArgumentException("Source address is different type to group"); |
|
910 |
|
911 int n; |
|
912 if (family == StandardProtocolFamily.INET6) { |
|
913 MembershipKeyImpl.Type6 key6 = |
|
914 (MembershipKeyImpl.Type6)key; |
|
915 n = Net.block6(fd, key6.group(), key6.index(), |
|
916 Net.inet6AsByteArray(source)); |
|
917 } else { |
|
918 MembershipKeyImpl.Type4 key4 = |
|
919 (MembershipKeyImpl.Type4)key; |
|
920 n = Net.block4(fd, key4.group(), key4.interfaceAddress(), |
|
921 Net.inet4AsInt(source)); |
|
922 } |
|
923 if (n == IOStatus.UNAVAILABLE) { |
|
924 // ancient kernel |
|
925 throw new UnsupportedOperationException(); |
|
926 } |
|
927 } |
|
928 } |
|
929 |
|
930 /** |
|
931 * Unblock given source. |
|
932 */ |
|
933 void unblock(MembershipKeyImpl key, InetAddress source) |
|
934 throws IOException |
|
935 { |
|
936 assert key.getChannel() == this; |
|
937 assert key.getSourceAddress() == null; |
|
938 |
|
939 synchronized (stateLock) { |
|
940 if (!key.isValid()) |
|
941 throw new IllegalStateException("key is no longer valid"); |
|
942 |
|
943 if (family == StandardProtocolFamily.INET6) { |
|
944 MembershipKeyImpl.Type6 key6 = |
|
945 (MembershipKeyImpl.Type6)key; |
|
946 Net.unblock6(fd, key6.group(), key6.index(), |
|
947 Net.inet6AsByteArray(source)); |
|
948 } else { |
|
949 MembershipKeyImpl.Type4 key4 = |
|
950 (MembershipKeyImpl.Type4)key; |
|
951 Net.unblock4(fd, key4.group(), key4.interfaceAddress(), |
|
952 Net.inet4AsInt(source)); |
|
953 } |
|
954 } |
|
955 } |
|
956 |
587 protected void implCloseSelectableChannel() throws IOException { |
957 protected void implCloseSelectableChannel() throws IOException { |
588 synchronized (stateLock) { |
958 synchronized (stateLock) { |
589 nd.preClose(fd); |
959 nd.preClose(fd); |
|
960 |
|
961 // if member of mulitcast group then invalidate all keys |
|
962 if (registry != null) |
|
963 registry.invalidateAll(); |
|
964 |
590 long th; |
965 long th; |
591 if ((th = readerThread) != 0) |
966 if ((th = readerThread) != 0) |
592 NativeThread.signal(th); |
967 NativeThread.signal(th); |
593 if ((th = writerThread) != 0) |
968 if ((th = writerThread) != 0) |
594 NativeThread.signal(th); |
969 NativeThread.signal(th); |