JDK-5073504 : (so spec) SelectableChannel.close spec should mention delayed resource release
  • Type: Enhancement
  • Component: core-libs
  • Sub-Component: java.nio
  • Affected Version: 1.4.2,5.0
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: linux,windows_2000,windows_xp
  • CPU: x86
  • Submitted: 2004-07-13
  • Updated: 2018-08-31
  • Resolved: 2018-08-31
Related Reports
Duplicate :  
Description

Name: jl125535			Date: 07/13/2004


FULL PRODUCT VERSION :
java version "1.5.0-beta3"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-beta3-b57)
Java HotSpot(TM) Client VM (build 1.5.0-beta3-b57, mixed mode)

java version "1.4.2"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2-b28)
Java HotSpot(TM) Client VM (build 1.4.2-b28, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600]

A DESCRIPTION OF THE PROBLEM :
There are no JavaDocs for ServerSocketChannel.close() -- the reader must default to the Channel.close() docs, which are not specific about the function of the close() method on a non-blocking ServerSocketChannel and the releasing of OS resources. Specifically, after closing a non-blocking ServerSocketChannel that has been registered with a Selector, the isOpen() method will return false. There is no reason for the developer to believe that the close operation is not complete at this point. However, the port has not been freed to the OS...and a subsequent open()/bind() to the same port number will fail. Even if you have de-registered the channel with the selector via SelectionKey.cancel(), the resource is still not freed.  You must make another call to Selector.select() in order for the resources to be freed. In our case, our application knows that there are no other resources registered to the Selector and so there was no reason (or so we thought) to make another call to the Selector.

This behavior is _hinted_ at in the Selector documentation...in this line:

"Each key in the cancelled-key set is removed from each key set of which it is a member, and its channel is deregistered."

However, that says nothing about completing a close operation or freeing underlying resources. Either the documentation needs to be more explicit or there is a bug with this behavior.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
I expect the ServerSocketChannel.close() method to free the port back to the OS.
ACTUAL -
After successfully executing a call to ServerSocketChannel.close(), the port is still in use.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
java.net.BindException: Address already in use: bind
	at sun.nio.ch.Net.bind(Native Method)
	at sun.nio.ch.ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:119)
	at sun.nio.ch.ServerSocketAdaptor.bind(ServerSocketAdaptor.java:59)
	at sun.nio.ch.ServerSocketAdaptor.bind(ServerSocketAdaptor.java:52)
	at test.Test.main(Test.java:37)




REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.nio.channels.*;
import java.net.*;
import java.io.*;

public class Test
    {
    public static void main(String[] args)
        {

        try
            {
            Selector _selector = Selector.open();

            ServerSocketChannel _server = ServerSocketChannel.open();
            _server.configureBlocking(false);
            _server.socket().bind(new InetSocketAddress(8080));
            SelectionKey key = 
                _server.register(_selector, SelectionKey.OP_ACCEPT);
            Thread.sleep(100);
            _server.close();
            key.cancel();

            if (_server.isOpen())
                System.out.println("The ServerSocket was not closed!");
            _server = null;
            Thread.sleep(100);

            // do this select...and it works!
            //_selector.select(100);

            _server = ServerSocketChannel.open();
            _server.configureBlocking(false);
            _server.socket().bind(new InetSocketAddress(8080));
            key = _server.register(_selector, SelectionKey.OP_ACCEPT);
            Thread.sleep(100);
            _server.close();
            key.cancel();
            _selector.close();
            }
        catch (IOException e)
            {
            System.out.println("Bang!");
            e.printStackTrace();
            }
        catch (InterruptedException e)
            {
            }
        }

    }



---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Make an extraneous call to Selector.select() after calling ServerSocketChannel.close().
(Incident Review ID: 285702) 
======================================================================

Comments
JDK-8207045 has been created to add an API note.
31-08-2018

EVALUATION The reported behavior is as intended, and is described in the specification of the SelectableChannel class, of which ServerSocketChannel is a subclass: Once registered with a selector, a channel remains registered until it is deregistered. This involves deallocating whatever resources were allocated to the channel by the selector. A channel cannot be deregistered directly; instead, the key representing its registration must be cancelled. Cancelling a key requests that the channel be deregistered during the selector's next selection operation. This can be confusing, however, so it would be worth mentioning the delayed release of OS resources in the specification of the SelectableChannel.close method. I've therefore changed this bug into a spec RFE. -- ###@###.### 2004/7/14
07-12-0180