HADOOP-19261. Support force close a DomainSocket for server service (#7057)

This commit is contained in:
Sammi Chen 2024-10-01 01:06:07 +08:00 committed by GitHub
parent 9aca73481e
commit 6fd4fea748
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 85 additions and 53 deletions

View File

@ -339,10 +339,13 @@ private static native void closeFileDescriptor0(FileDescriptor fd)
private static native void shutdown0(int fd) throws IOException; private static native void shutdown0(int fd) throws IOException;
/** /**
* Close the Socket. * Close the Server Socket without check refCount.
* When Server Socket is blocked on accept(), its refCount is 1.
* close() call on Server Socket will be stuck in the while loop count check.
* @param force if true, will not check refCount before close socket.
* @throws IOException raised on errors performing I/O.
*/ */
@Override public void close(boolean force) throws IOException {
public void close() throws IOException {
// Set the closed bit on this DomainSocket // Set the closed bit on this DomainSocket
int count; int count;
try { try {
@ -351,9 +354,20 @@ public void close() throws IOException {
// Someone else already closed the DomainSocket. // Someone else already closed the DomainSocket.
return; return;
} }
boolean interrupted = false;
if (force) {
try {
// Calling shutdown on the socket will interrupt blocking system
// calls like accept, write, and read that are going on in a
// different thread.
shutdown0(fd);
} catch (IOException e) {
LOG.error("shutdown error: ", e);
}
} else {
// Wait for all references to go away // Wait for all references to go away
boolean didShutdown = false; boolean didShutdown = false;
boolean interrupted = false;
while (count > 0) { while (count > 0) {
if (!didShutdown) { if (!didShutdown) {
try { try {
@ -373,6 +387,7 @@ public void close() throws IOException {
} }
count = refCount.getReferenceCount(); count = refCount.getReferenceCount();
} }
}
// At this point, nobody has a reference to the file descriptor, // At this point, nobody has a reference to the file descriptor,
// and nobody will be able to get one in the future either. // and nobody will be able to get one in the future either.
@ -387,6 +402,14 @@ public void close() throws IOException {
} }
} }
/**
* Close the Socket.
*/
@Override
public void close() throws IOException {
close(false);
}
/** /**
* Call shutdown(SHUT_RDWR) on the UNIX domain socket. * Call shutdown(SHUT_RDWR) on the UNIX domain socket.
* *

View File

@ -20,7 +20,6 @@
import java.io.Closeable; import java.io.Closeable;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.Random;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.hadoop.fs.FileUtil; import org.apache.hadoop.fs.FileUtil;
@ -35,8 +34,7 @@ public class TemporarySocketDirectory implements Closeable {
public TemporarySocketDirectory() { public TemporarySocketDirectory() {
String tmp = System.getProperty("java.io.tmpdir", "/tmp"); String tmp = System.getProperty("java.io.tmpdir", "/tmp");
dir = new File(tmp, "socks." + (System.currentTimeMillis() + dir = new File(tmp, "socks." + System.nanoTime());
"." + (new Random().nextInt())));
dir.mkdirs(); dir.mkdirs();
FileUtil.setWritable(dir, true); FileUtil.setWritable(dir, true);
} }

View File

@ -130,7 +130,7 @@ public Void call(){
DomainSocket conn = DomainSocket.connect(serv.getPath()); DomainSocket conn = DomainSocket.connect(serv.getPath());
Thread.sleep(50); Thread.sleep(50);
conn.close(); conn.close();
serv.close(); serv.close(true);
future.get(2, TimeUnit.MINUTES); future.get(2, TimeUnit.MINUTES);
} }
@ -161,7 +161,7 @@ public Void call(){
}; };
Future<Void> future = exeServ.submit(callable); Future<Void> future = exeServ.submit(callable);
Thread.sleep(500); Thread.sleep(500);
serv.close(); serv.close(true);
future.get(2, TimeUnit.MINUTES); future.get(2, TimeUnit.MINUTES);
} }
@ -240,7 +240,7 @@ public Void call(){
Future<Void> clientFuture = exeServ.submit(clientCallable); Future<Void> clientFuture = exeServ.submit(clientCallable);
Thread.sleep(500); Thread.sleep(500);
clientConn.close(); clientConn.close();
serv.close(); serv.close(true);
clientFuture.get(2, TimeUnit.MINUTES); clientFuture.get(2, TimeUnit.MINUTES);
serverFuture.get(2, TimeUnit.MINUTES); serverFuture.get(2, TimeUnit.MINUTES);
} }
@ -281,7 +281,6 @@ public void testServerOptions() throws Exception {
final String TEST_PATH = new File(sockDir.getDir(), final String TEST_PATH = new File(sockDir.getDir(),
"test_sock_server_options").getAbsolutePath(); "test_sock_server_options").getAbsolutePath();
DomainSocket serv = DomainSocket.bindAndListen(TEST_PATH); DomainSocket serv = DomainSocket.bindAndListen(TEST_PATH);
try {
// Let's set a new receive buffer size // Let's set a new receive buffer size
int bufSize = serv.getAttribute(DomainSocket.RECEIVE_BUFFER_SIZE); int bufSize = serv.getAttribute(DomainSocket.RECEIVE_BUFFER_SIZE);
int newBufSize = bufSize / 2; int newBufSize = bufSize / 2;
@ -293,17 +292,29 @@ public void testServerOptions() throws Exception {
serv.setAttribute(DomainSocket.RECEIVE_TIMEOUT, newTimeout); serv.setAttribute(DomainSocket.RECEIVE_TIMEOUT, newTimeout);
int nextTimeout = serv.getAttribute(DomainSocket.RECEIVE_TIMEOUT); int nextTimeout = serv.getAttribute(DomainSocket.RECEIVE_TIMEOUT);
Assert.assertEquals(newTimeout, nextTimeout); Assert.assertEquals(newTimeout, nextTimeout);
ExecutorService exeServ = Executors.newSingleThreadExecutor();
Callable<Void> callable = new Callable<Void>() {
public Void call() {
try { try {
serv.accept(); serv.accept();
Assert.fail("expected the accept() to time out and fail"); Assert.fail("expected the accept() to time out and fail");
} catch (SocketTimeoutException e) { } catch (SocketTimeoutException e) {
GenericTestUtils.assertExceptionContains("accept(2) error: ", e); GenericTestUtils.assertExceptionContains("accept(2) error: ", e);
} catch (AsynchronousCloseException e) {
return null;
} catch (IOException e) {
throw new RuntimeException("unexpected IOException", e);
} }
} finally { return null;
serv.close(); }
};
Future<Void> future = exeServ.submit(callable);
Thread.sleep(500);
serv.close(true);
future.get();
Assert.assertFalse(serv.isOpen()); Assert.assertFalse(serv.isOpen());
} }
}
/** /**
* A Throwable representing success. * A Throwable representing success.
@ -656,7 +667,7 @@ public void run(){
} }
serverThread.join(120000); serverThread.join(120000);
clientThread.join(120000); clientThread.join(120000);
serv.close(); serv.close(true);
for (PassedFile pf : passedFiles) { for (PassedFile pf : passedFiles) {
pf.cleanup(); pf.cleanup();
} }