View Issue Details

IDProjectCategoryView StatusLast Update
0001690libmicrohttpdthread poolpublic2011-07-07 23:30
Reporterbplant Assigned ToChristian Grothoff  
PrioritynormalSeverityfeatureReproducibilityN/A
Status closedResolutionwon't fix 
Product Version0.9.5 
Summary0001690: Queue new connections when max connection limit is reached
DescriptionI am using MHD in thread-per-connection mode on an embedded processor than has only 96KB of ram. As such, I cannot create many threads and therefore cannot set MHD's connection limit very high. The problem I'm finding is that web browsers send off lots of parallel requests to the web server (fetching images, javascript and css files) which is causing the maximum connection limit to be reached randomly.

Currently when the connection limit is reached, the connection is closed. It would be better if the connection was instead queued and serviced when an in progress connection finished.

As a *rough* proof of concept, I am checking daemon->max_connections at the start of MHD_accept_connection(). If daemon->max_connections == 0, I'm sleeping for 100ms and returning MHD_NO. This is allowing me to run MHD with a max connection limit of only 3.
TagsNo tags attached.

Activities

Christian Grothoff

2011-06-17 11:33

manager   ~0004406

Actually, this should not be a big issue since there is an OS mechanism for this that MHD is using: the listen queue. If MHD has too many connections, it stops *accepting* connections from the listen socket (at least that's what should happen by design, not sure it has been tested well). The OS then queues the connections, until the threshold given to the "listen" call is exceeded. MHD defaults to 5 here, but you can change this by passing a pre-initialized listen socket to the daemon start call.

bplant

2011-06-17 12:03

reporter   ~0004418

The behaviour that you describe is what I'm looking for. Unfortunately, it's not currently happening. The connection is getting closed in MHD_add_connection()

  if ((daemon->max_connections == 0)
      || (MHD_ip_limit_add (daemon, addr, addrlen) == MHD_NO))
    {
      /* above connection limit - reject */
#if HAVE_MESSAGES
      MHD_DLOG (daemon,
                "Server reached connection limit (closing inbound connection)\n");
#endif
      SHUTDOWN (client_socket, SHUT_RDWR);
      CLOSE (client_socket);
      return MHD_NO;
    }

It looks like connections would be queued when *not* operating in MHD_USE_THREAD_PER_CONNECTION:

  if (0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
    {
      /* single-threaded, go over everything */
      if (MHD_NO == MHD_get_fdset (daemon, &rs, &ws, &es, &max))
        return MHD_NO;

      /* If we're at the connection limit, no need to
         accept new connections. */
      if ( (daemon->max_connections == 0) && (daemon->socket_fd != -1) )
        FD_CLR(daemon->socket_fd, &rs);
    }
  else
    {
      /* accept only, have one thread per connection */
      max = daemon->socket_fd;
      if (max == -1)
        return MHD_NO;
      FD_SET (max, &rs);
    }

Christian Grothoff

2011-06-22 10:54

manager   ~0004449

Ah, yes. Well, the MHD_ip_limit-check we cannot do until after we've accepted, so that one will need to stay anyway. In the MHD_USE_THREAD_PER_CONNECTION case, yes, that's an issue since it will be a bit tricky to wake up the master thread upon the termination of "any" of the client connections.

I think the way to do it with 'poll' would be for the master thread to 'poll' on *all* client FDs (for SIGHUP-only). However, for 'select', I cannot think of a good solution (that wouldn't introduce something like pipes, which are again problematic on your platform).

What prevents you from using the internal/external-select modes instead of thread-per-connection? That would save you the stack space for the threads AND elimiante this problem...

bplant

2011-06-23 09:58

reporter   ~0004454

Which mode would you recommend? The web server needs to be able to handle more than one connection at a time. I.e. It still needs to be able to handle connections if a client is downloading a large file.

Christian Grothoff

2011-06-23 11:25

manager   ~0004455

You may be able to handle 100s of parallel connections with 'external select' mode -- and you won't even need threads. I generally would recommend to people to start with 'external select' just because it avoid threads altogether. Then move from select to poll if you have trouble (and poll is available on your platform). Overall, on small embedded systems I don't really think any of the threaded modes make any sense --- for performance, they are really only for systems with many cores.

Christian Grothoff

2011-07-07 23:30

manager   ~0004486

I'm marking this as "won't fix" for now, given that I don't have a solution that would not introduce additional problems and have high complexity AND that I believe that there is a feasible alternative. This does not mean that given the right argument / patch, I could not be convinced otherwise.

Issue History

Date Modified Username Field Change
2011-06-14 08:44 bplant New Issue
2011-06-17 11:33 Christian Grothoff Note Added: 0004406
2011-06-17 11:33 Christian Grothoff Status new => feedback
2011-06-17 12:03 bplant Note Added: 0004418
2011-06-22 10:54 Christian Grothoff Note Added: 0004449
2011-06-23 09:58 bplant Note Added: 0004454
2011-06-23 11:25 Christian Grothoff Note Added: 0004455
2011-07-07 23:30 Christian Grothoff Note Added: 0004486
2011-07-07 23:30 Christian Grothoff Assigned To => Christian Grothoff
2011-07-07 23:30 Christian Grothoff Reproducibility always => N/A
2011-07-07 23:30 Christian Grothoff Status feedback => closed
2011-07-07 23:30 Christian Grothoff Resolution open => won't fix
2011-07-07 23:30 Christian Grothoff Category other => multi-threaded operation
2013-05-06 12:53 Christian Grothoff Category multi-threaded operation => libmicrohttpd multi-threaded operation
2024-01-21 13:25 Christian Grothoff Category libmicrohttpd multi-threaded operation => thread pool