0

Checking man epoll(7) I see a mysterious line: setnonblocking(conn_sock);

#define MAX_EVENTS 10
struct epoll_event ev, events[MAX_EVENTS];
int listen_sock, conn_sock, nfds, epollfd;

/* Code to set up listening socket, 'listen_sock',
  (socket(), bind(), listen()) omitted. */

epollfd = epoll_create1(0);
if (epollfd == -1) {
   perror("epoll_create1");
   exit(EXIT_FAILURE);
}

ev.events = EPOLLIN;
ev.data.fd = listen_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {
   perror("epoll_ctl: listen_sock");
   exit(EXIT_FAILURE);
}

for (;;) {
   nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
   if (nfds == -1) {
       perror("epoll_wait");
       exit(EXIT_FAILURE);
   }

   for (n = 0; n < nfds; ++n) {
       if (events[n].data.fd == listen_sock) {
           conn_sock = accept(listen_sock,
                              (struct sockaddr *) &addr, &addrlen);
           if (conn_sock == -1) {
               perror("accept");
               exit(EXIT_FAILURE);
           }
           setnonblocking(conn_sock);
           ev.events = EPOLLIN | EPOLLET;
           ev.data.fd = conn_sock;
           if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,
                       &ev) == -1) {
               perror("epoll_ctl: conn_sock");
               exit(EXIT_FAILURE);
           }
       } else {
           do_use_fd(events[n].data.fd);
       }
   }
}

AFAIK epoll_wait already causes function accept to be non-blocking, or rather, it allows the detection of ready-to-use fds, so the server can already serve concurrently all connected clients. I don't understand this manipulation of the file descriptor, what is this function supposed to do?

David Ranieri
  • 39,972
  • 7
  • 52
  • 94

1 Answers1

1

AFAIK epoll already causes function accept to be non-blocking

And as far as I know, that is not correct. I don't see anything in the man page documentation for epoll_ctl or epoll(7) that would suggest that. There's plenty of references in epoll(7) that suggest using non-blocking file descriptors.

Regardless, I'm guessing setnonblocking is just a function that does this:

int setnonblocking(sock)
{
    int result;
    int flags;

    flags = ::fcntl(_sock, F_GETFL, 0);

    if (flags == -1)
    {
        return -1;  // error
    }

    flags |= O_NONBLOCK;

    result = fcntl(_sock , F_SETFL , flags);
    return result;
}

I just a similar version of that above function in my own code that uses epoll for TCP sockets. I apply the above function on both the listen socket that is passed to accept as well as the connection sockets returned from accept.

selbie
  • 100,020
  • 15
  • 103
  • 173
  • sockets default to blocking. The last thing I want to do is starve a thread and have a socket block becaues of a spurious wakeup from whatever polling/triggering mechanism is hinting at data being available (or because of some other bug in my code that polled the blocking socket again). Hence, for an high-performance socket server, you really want to make all sockets non-blocking and have your code handle EAGAIN or EWOULDBLOCK errors as benign. – selbie Sep 28 '22 at 10:18