Search code examples
csocketsepoll

setnonblocking on epoll man page


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?


Solution

  • 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.