Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-24778

running out of file descriptors causes QTcpServer to hang on accept/poll loop

    XMLWordPrintable

Details

    • Bug
    • Resolution: Done
    • P2: Important
    • 5.0.0 Beta 2
    • 4.7.3
    • Network: Sockets
    • None
    • 4920090da0fc937d171065694d5274221d2747c9.

    Description

      Use the unmodified TCP server example: http://qt-project.org/doc/qt-4.8/qt4-network.html#tcp-server

      Use "ulimit -n 10" to make it easy to cause accept(2) to return EMFILE (Too many open files).

      Run the TCP server. From another window, connect to it 10 times: for i in seq 10; do nc 127.0.0.1 1234 & done

      The TCP server will go into an infinite loop, taking 100% CPU. strace shows:

      accept(7, 0, NULL) = -1 EMFILE (Too many open files)
      poll([

      {fd=3, events=POLLIN}, <snip>])
      accept(7, 0, NULL) = -1 EMFILE (Too many open files)
      poll([{fd=3, events=POLLIN}

      , <snip>])
      ...

      This means that a QTcpServer-based server can be permanently disabled, even if it only temporarily runs out of file descriptors.

      The fact that accept(2) is failing is correct. The fact that no more connections can be accepted is correct, temporarily. But the correct behavior in this case is that once some client connections are closed, the server should gracefully recover and start accepting connections again.

      The problem is that QTcpServerPrivate::readNotification, returns when it gets an error from socketEngine->accept(). It returns without addressing the fact that the listen socket is readable. As a result, it is called again immediately from the event loop. A better solution would be to provide a signal called "acceptError" that the caller can hook up to a slot. This slot needs to call QTcpServer::close() in order to avoid the loop, and then try some strategy to start listening again (perhaps use a timer to try QTcpServer::listen() again later, or perhaps use some other state in the application to know when to try listening again).

      It appears that when a client connection disconnects, its socket becomes readable and, depending on how the event loop schedules the work, it might unblock the accept/poll loop. But until this happens, the server is wasting cycles doing the loop.

      (Thanks to my colleague Marko Peltonen for researching this and proposing this solution.)

      Attachments

        Issue Links

          No reviews matched the request. Check your Options in the drop-down menu of this sections header.

          Activity

            People

              gastal Jonas Mauricio Gastal
              jeffall Jeff Allen
              Votes:
              0 Vote for this issue
              Watchers:
              5 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:

                Gerrit Reviews

                  There are no open Gerrit changes