JEP draft: Reimplement the Legacy DatagramSocket API

OwnerDaniel Fuchs
TypeFeature
ScopeJDK
StatusSubmitted
Componentcore-libs / java.net
Discussionnet dash dev at openjdk dot java dot net
EffortS
Reviewed byAlan Bateman, Chris Hegarty
Created2019/12/10 16:13
Updated2020/02/12 11:51
Issue8235674

Summary

Replace the underlying implementation of java.net.DatagramSocket and java.net.MulticastSocket APIs with a simpler and more modern implementation that is easy to maintain and debug. The new implementation will be easy to adapt to work with virtual threads, currently being explored in Project Loom. This is a follow-on to JEP 353 which already reimplemented the legacy Socket API.

Motivation

The java.net.DatagramSocket and java.net.MulticastSocket APIs, and their underlying implementations, date back to JDK 1.0. The implementation is a mix of legacy Java and C code that is difficult to maintain and debug. The implementation of MulticastSocket is particularly problematic as it dates back to a time where IPv6 was still under development and much of the underlying native implementation tries to reconcile IPv4 and IPv6 in ways that are difficult to maintain. The implementation also has several concurrency issues that require an overhaul to address properly. In the context of a future world of virtual threads that park instead of blocking the underlying kernel threads in system calls, the current implementation is not fit for purpose. Additionally, in a context where datagram-based transports are gaining traction again, e.g. QUIC, a more modern and maintainable implementation is needed.

Description

The DatagramSocket and MulticastSocket classes eventually delegate all socket calls to an implementation implementing the java.net.DatagramSocketImpl interface, for which different platform-specific implementations exists: PlainDatagramSocketImpl on Unix platforms, TwoStackPlainDatagramSocketImpl and DualPlainDatagramSocketImpl on Microsoft Windows platforms. The DatagramSocketImpl interface however dates back to JDK 1.1 and contains many obsolete APIs (such as the single argument join/leave methods) as well as other APIs (such as peekData) that would be an impediment when trying to provide an nio-based implementation of this class (see alternatives below). Rather than providing a drop-in replacement for implementations of DatagramSocketImpl, this JEP therefore proposes to make DatagramSocket "wrap" another instance of DatagramSocket to which it would delegate all calls directly. The wrapped instance would be either a socket adapter created from a DatagramChannel (new implementation) - or a clone of the legacy DatagramSocket, which would then delegate to the legacy DatagramSocketImpl (for the purpose of implementing a backward compatibility switch). Should a DatagramSocketImplFactory be installed by an application, the old legacy implementation will be selected. Otherwise, the new implementation would be selected and used by default. The plan is to eventually deprecate java.net.DatagramSocketImpl and java.net.DatagramSocketImplFactory - either as part of this JEP, or as preparatory work, or as a followup.

Switching to the new implementation should be mostly transparent for regular users of the API. To ensure a smooth transition the new implementation should therefore pass the tier2 (jdk_net and jdk_nio) regression test suite and the JCK for java_net/api. The new implementation should be enabled by default. To minimize risks, the new implementation should also allow the legacy (net-based) implementation to coexist with the new (nio-based) implementation, allowing to select one or the other with the system property -Djdk.net.usePlainDatagramSocketImpl=true | false (see Risks And Assumptions below).

The new implementation provides non-interruptible behavior to datagram and multicast sockets by using directly the platform default implementation of the selector provider (sun.nio.ch.SelectorProviderImpl and sun.nio.ch.DatagramChannelImpl), making use of the changes brought by JDK-8236246.

Alternatives

Three different alternatives have been investigated and prototyped:

Prototype 1 showed that it would be relatively easy to provide an implementation of java.net.DatagramSocketImpl based on DatagramChannel. Tests where passing, but it also highlighted some limitations:

Prototype 2 took an alternative route, and provide an implementation of DatagramSocketImpl that would live in the sun.nio.ch package, allowing it to access lower level NIO primitives instead of relying on DatagramChannel. This was somewhat analogous to what was done for reimplementing Socket/ServerSocket in JEP 353:

Prototype 3 - the solution eventually proposed for this JEP, is an evolution of prototype 1 which alters DatagramSocket to delegate to an other instance of DatagramSocket. When public DatagramSocket class constructors are used, the delegated instance can be a simple DatagramChannel::socket adapter.

Testing

The existing tests in the jdk/jdk repository will be used to test the new implementation. The jdk_net test group has accumulated many tests for networking corner case scenarios over the years. Some of the tests in this test group will be modified to run twice, the second time with -Djdk.net.usePlainDatagramSocketImpl to ensure that the old implementation does not bit-rot during the time that the JDK includes both implementations. New tests will be added as required, to expand code coverage and increase confidence in the new implementation.

Every effort will be made to create awareness of the proposal and encourage developers that have code using DatagramSocket and MulticastSocket to test their code with the early-access builds that are published on jdk.java.net or elsewhere.

The microbenchmarks in the jdk/jdk repository include benchmarks for datagram channel. Similar benchmarks for datagram socket will be created if missing (or updated if existing) in a way that make it easy to compare the old and new implementations.

Risks and Assumptions

The primary risk of this proposal is that there is existing code that depends on unspecified behavior in corner cases where the old and new implementations behave differently. To minimize risks, some preparatory work to clarify the specification of java.net.DatagramSocket and java.net.MulticastSocket and minimize the behavioral differences between these classes and the java.nio.channels.DatagramChannel::socket adapter has already been implemented in JDK 14 and JDK 15. Some small differences listed below might however persists. To further mitigate the risk the old legacy "java.net" implementation can still be selected by running with either -Djdk.net.usePlainDatagramSocketImpl or -Djdk.net.usePlainDatagramSocketImpl=true.

Other observable behavioral differences:

Aside from behavioral differences, the performance of the new implementation may differ to the old when running certain workloads. This JEP will endeavor to provide some performance benchmarks to gauge the difference.