In your example the server never reads from the socket. That means the send buffer on the client eventually fills up, and further write calls block.
When you later call close(sock), Julia’s close wraps uv_close from libuv. uv_close tries to flush/clean up any pending writes before completing. But because the server isn’t reading, those writes never drain. As a result, the close call hangs forever.
A workaround is to call shutdown(2) explicitly on the client socket before closing it. This tells the kernel to immediately tear down one or both directions of the TCP connection without waiting for buffered data to be delivered. That avoids libuv’s flush loop and lets Julia’s close return.
Note, however, that this will leave the server-side connection open. The server won’t notice the shutdown until it eventually attempts to read from or write to the socket.
using Sockets
# <sys/socket.h>
# #define SHUT_RDWR 2
const SHUT_RDWR = 2
function shutdown_close(sock)
sockfd = Base._fd(sock)
ret = ccall(:shutdown, Cint, (Cint, Cint), sockfd, SHUT_RDWR)
if ret != 0
err = Libc.errno()
error("shutdown($sockfd, $how) failed errno=$err")
end
close(sock)
return nothing
end
s = listen(1234)
try
local c, cs
@sync begin
@async cs = accept(s)
c = connect(1234)
end
try
i = 0
data = zeros(UInt8, 1000)
while true
t = Timer(_ -> shutdown_close(c), 1)
write(c, data)
close(t)
i += 1
println(i)
end
finally
close(cs)
close(c)
end
finally
close(s)
end