try-catch-finally
try-catch-finally

Reputation: 7634

strace: Any sense in `dup2(A, B); close(B)`?

I'm trying to understand a strace of an Java application server running in a oracle JVM.

I often see these lines:

[pid 10465] 23:04:59.658453 dup2(215, 274) = 274
[pid 10465] 23:04:59.658616 close(274)  = 0

In this case 215 is a UNIX socket:

java    10387 XXX  215u  unix 0xffff880037962cc0      0t0 153294021 socket

FD 274 is an open TCP socket.

These "call pairs" repeat multiple times with the same A socket over multiple minutes.

My understanding of dup2(A, B) is that it creates an file descriptor B pointing/refering to the same file/socket A, closing B first if it's still open.

Opengroup says

The dup2() function shall cause the file descriptor fildes2 to refer to the same open file description as the file descriptor fildes and to share any locks, and shall return fildes2. If fildes2 is already a valid open file descriptor, it shall be closed first, unless fildes is equal to fildes2 in which case dup2() shall return fildes2 without closing it.

My only guess so far is, that this a strange, unoptimized behaviour of the JVM. Isn't it? Which sense does it make.

If necessary, I'll add more context calls.

Upvotes: 6

Views: 861

Answers (1)

apangin
apangin

Reputation: 98610

This sequence provides a thread-safe way to close a socket.

dup2 allows to close a socket without releasing a file descriptor. This can be a lengthy operation due to untransmitted data and a long linger interval. dup2(A, B) is an atomic equivalent to

close(B);
fcntl(A, F_DUPFD, B);

i.e. B remains a valid descriptor at any time. Keeping the file descriptor valid is essential until all threads that have acquired this descriptor complete outstanding I/O operations.

fd being duplicated (215 in your case) is a special marker socket in shutdown state. It is created once for the only purpose of closing other sockets via dup2. Once dup2 is complete, any reads from the original fd (274) will return EOF and any writes to it will get an error.

Java maintains a use counter for each file descriptor. Threads increment this counter when acquiring a file descriptor and decrement it when they are done with I/O. As soon as use counter drops to 0, Java calls close to release the file descriptor and make it available to the OS.

Java NIO implementation uses the same technique to close files.
Though I haven't seen this pattern other than in JDK source code.

Upvotes: 7

Related Questions