libzmq icon indicating copy to clipboard operation
libzmq copied to clipboard

Closing sockets and terminating context after fork leaks fds

Open dcgloe opened this issue 4 years ago • 2 comments

Issue description

After forking, closing sockets and terminating the context leaves file descriptors open. I believe this is because the socket close expects a separate thread to handle closing the fd, but the thread is gone after fork.

Environment

  • libzmq version (commit hash if unreleased):
  • OS:

Minimal test code / Steps to reproduce the issue

To reproduce, compile and run the following code, and inspect open fds with lsof.

#include <unistd.h>
#include <zmq.h>

int main() {
	void *ctx = zmq_ctx_new();
	void *sock = zmq_socket(ctx, ZMQ_ROUTER);
	zmq_bind(sock, "tcp://*:7919");
	pid_t pid = fork();
	if (pid == 0) {
		zmq_close(sock);
		zmq_ctx_term(ctx);
	}
	sleep(300);
	return 0;
}

What's the actual result? (include assertion message & call stack if applicable)

The child process still has these file descriptors open:

zeromqfor 17872 root    3u  a_inode     0,13        0   9013 [eventfd]
zeromqfor 17872 root    4u  a_inode     0,13        0   9013 [eventfd]
zeromqfor 17872 root    5u  a_inode     0,13        0   9013 [eventpoll]
zeromqfor 17872 root    6u  a_inode     0,13        0   9013 [eventfd]
zeromqfor 17872 root    7u  a_inode     0,13        0   9013 [eventpoll]
zeromqfor 17872 root    8u  a_inode     0,13        0   9013 [eventfd]
zeromqfor 17872 root    9u     IPv4 22940606      0t0    TCP *:7919 (LISTEN)

What's the expected result?

The zmq_close and zmq_ctx_term in the child should close these file descriptors.

dcgloe avatar Jan 29 '21 19:01 dcgloe

I may be wrong here re zmq specifics, but generally when handles have a lifetime intersected by a fork() then both parent and client need to close the handle. i.e.

int handle = open(...);
pid = fork();
// now <handle> is 'live' in both parent and child.
// *One* side is supposed to do something with it, re *work*, e.g.
if (pid) {
  do_work_with(handle);
  close(handle);
} else {
  // close *immediately*
  close(handle);
}

Without knowing the particulars of zmq (just new here!), the above is the generic pattern around a fork, so that would make your code look like this:

if (pid == 0) {
  zmq_close(sock);
  zmq_ctx_term(ctx);
} else {
  do_your_work_with(sock);
  zmq_close(sock);
  zmq_ctx_term(ctx);
}

GerHobbelt avatar Jan 31 '21 20:01 GerHobbelt

This issue has been automatically marked as stale because it has not had activity for 365 days. It will be closed if no further activity occurs within 56 days. Thank you for your contributions.

stale[bot] avatar Apr 16 '22 17:04 stale[bot]