View Issue Details

IDProjectCategoryView StatusLast Update
0003126libmicrohttpdexternal APIpublic2013-12-04 18:54
Reportermatt.holiday Assigned ToChristian Grothoff  
PrioritynormalSeverityminorReproducibilityalways
Status closedResolutionfixed 
PlatformLinux 
Product VersionGit master 
Target Version0.9.32Fixed in Version0.9.32 
Summary0003126: Issues with new socket suspend/resume API
DescriptionEither sockets don't wake up or they don't suspend properly; patch attached based on svn 30760
Steps To ReproduceUse new experimental suspend/resume API, primarily in the thread pool case
Problems found both using select and epoll
Additional InformationChanges:

internal.h

1. define MHD_EPOLL_STATE_SUSPENDED
2. fix definition of MHD_TLS_CONNECTION_INIT

connection.c

1. check MHD_EPOLL_STATE_SUSPENDED in 4 places before EDLL_insert
2. also check it before doing an epoll add in the update function

daemon.c

1. mutex around DLL_remove etc.
2. check MHD_USE_EPOLL_LINUX_ONLY in suspend/resume epoll logic
3. set/clear MHD_EPOLL_STATE_SUSPENDED there
4. panic on failure to re-add fd to epoll in resume
5. on worker create, give each their own wpipe
6. use mutex in clean, and set state to MHD_CONNECTION_IN_CLEANUP
7. in shutdown, signal to the worker pipes after setting shutdown flag
8. in shutdown, don't check fd==-1 for worker epoll
9. in shutdown, delete the worker pipes
TagsNo tags attached.
Attached Files
libmicrohttpd.diff (10,545 bytes)   
Index: src/microhttpd/internal.h
===================================================================
--- src/microhttpd/internal.h	(revision 30839)
+++ src/microhttpd/internal.h	(working copy)
@@ -116,8 +116,12 @@
     /**
      * Is this connection currently in the 'epoll' set?
      */
-    MHD_EPOLL_STATE_IN_EPOLL_SET = 8
+    MHD_EPOLL_STATE_IN_EPOLL_SET = 8,
 
+    /**
+     * Is this connection currently suspended?
+     */
+    MHD_EPOLL_STATE_SUSPENDED = 16
   };
 
 
@@ -452,7 +456,7 @@
    * Handshake messages will be processed in this state & while
    * in the 'MHD_TLS_HELLO_REQUEST' state
    */
-  MHD_TLS_CONNECTION_INIT = MHD_CONNECTION_CLOSED + 1
+  MHD_TLS_CONNECTION_INIT = MHD_CONNECTION_IN_CLEANUP + 1
 
 };
 
Index: src/microhttpd/connection.c
===================================================================
--- src/microhttpd/connection.c	(revision 30839)
+++ src/microhttpd/connection.c	(working copy)
@@ -2474,6 +2474,7 @@
     {
     case MHD_EVENT_LOOP_INFO_READ:
       if ( (0 != (connection->epoll_state & MHD_EPOLL_STATE_READ_READY)) &&
+           (0 == (connection->epoll_state & MHD_EPOLL_STATE_SUSPENDED)) &&
 	   (0 == (connection->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL)) )
 	{
 	  EDLL_insert (daemon->eready_head,
@@ -2485,6 +2486,7 @@
     case MHD_EVENT_LOOP_INFO_WRITE:
       if ( (connection->read_buffer_size > connection->read_buffer_offset) &&
 	   (0 != (connection->epoll_state & MHD_EPOLL_STATE_READ_READY)) &&
+           (0 == (connection->epoll_state & MHD_EPOLL_STATE_SUSPENDED)) &&
 	   (0 == (connection->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL)) )
 	{
 	  EDLL_insert (daemon->eready_head,
@@ -2493,6 +2495,7 @@
 	  connection->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL;
 	}
       if ( (0 != (connection->epoll_state & MHD_EPOLL_STATE_WRITE_READY)) &&
+           (0 == (connection->epoll_state & MHD_EPOLL_STATE_SUSPENDED)) &&
 	   (0 == (connection->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL)) )
 	{
 	  EDLL_insert (daemon->eready_head,
@@ -2504,7 +2507,8 @@
     case MHD_EVENT_LOOP_INFO_BLOCK:
       /* we should look at this connection again in the next iteration
 	 of the event loop, as we're waiting on the application */
-      if (0 == (connection->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL))
+      if ( (0 == (connection->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL) &&
+           (0 == (connection->epoll_state & MHD_EPOLL_STATE_SUSPENDED))) )
 	{
 	  EDLL_insert (daemon->eready_head,
 		       daemon->eready_tail,
@@ -2539,6 +2543,7 @@
 
   if ( (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY)) &&
        (0 == (connection->epoll_state & MHD_EPOLL_STATE_IN_EPOLL_SET)) &&
+       (0 == (connection->epoll_state & MHD_EPOLL_STATE_SUSPENDED)) &&
        ( (0 == (connection->epoll_state & MHD_EPOLL_STATE_WRITE_READY)) ||
 	 ( (0 == (connection->epoll_state & MHD_EPOLL_STATE_READ_READY)) &&
 	   ( (MHD_EVENT_LOOP_INFO_READ == connection->event_loop_info) ||
Index: src/microhttpd/daemon.c
===================================================================
--- src/microhttpd/daemon.c	(revision 30839)
+++ src/microhttpd/daemon.c	(working copy)
@@ -1398,6 +1398,9 @@
   daemon = connection->daemon;
   if (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION))
     MHD_PANIC ("Cannot suspend connections in THREAD_PER_CONNECTION mode!\n");
+  if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+       (0 != pthread_mutex_lock (&daemon->cleanup_connection_mutex)) )
+    MHD_PANIC ("Failed to acquire cleanup mutex\n");
   DLL_remove (daemon->connections_head,
               daemon->connections_tail,
               connection);
@@ -1410,22 +1413,29 @@
                  daemon->manual_timeout_tail,
                  connection);
 #if EPOLL_SUPPORT
-  if (0 != (connection->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL))
-    {
-      EDLL_remove (daemon->eready_head,
-                   daemon->eready_tail,
-                   connection);
-    }
-  if (0 != (connection->epoll_state & MHD_EPOLL_STATE_IN_EPOLL_SET))
-    {
-      if (0 != epoll_ctl (daemon->epoll_fd,
-                          EPOLL_CTL_DEL,
-                          connection->socket_fd,
-                          NULL))
-        MHD_PANIC ("Failed to remove FD from epoll set\n");
-      connection->epoll_state &= ~MHD_EPOLL_STATE_IN_EPOLL_SET;
-    }
+  if (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY))
+  {
+    if (0 != (connection->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL))
+      {
+        EDLL_remove (daemon->eready_head,
+                     daemon->eready_tail,
+                     connection);
+      }
+    if (0 != (connection->epoll_state & MHD_EPOLL_STATE_IN_EPOLL_SET))
+      {
+        if (0 != epoll_ctl (daemon->epoll_fd,
+                            EPOLL_CTL_DEL,
+                            connection->socket_fd,
+                            NULL))
+          MHD_PANIC ("Failed to remove FD from epoll set\n");
+        connection->epoll_state &= ~MHD_EPOLL_STATE_IN_EPOLL_SET;
+      }
+    connection->epoll_state |= MHD_EPOLL_STATE_SUSPENDED;
+  }
 #endif
+  if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+       (0 != pthread_mutex_unlock (&daemon->cleanup_connection_mutex)) )
+    MHD_PANIC ("Failed to release cleanup mutex\n");
 }
 
 
@@ -1460,33 +1470,30 @@
                  daemon->manual_timeout_tail,
                  connection);
 #if EPOLL_SUPPORT
-  if (0 != (connection->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL))
-    {
-      EDLL_insert (daemon->eready_head,
-                   daemon->eready_tail,
-                   connection);
-    }
-  else
-    {
-      struct epoll_event event;
+  if (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY))
+  {
+    if (0 != (connection->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL))
+      {
+        EDLL_insert (daemon->eready_head,
+                     daemon->eready_tail,
+                     connection);
+      }
+    else
+      {
+        struct epoll_event event;
 
-      event.events = EPOLLIN | EPOLLOUT | EPOLLET;
-      event.data.ptr = connection;
-      if (0 != epoll_ctl (daemon->epoll_fd,
-                          EPOLL_CTL_ADD,
-                          connection->socket_fd,
-                          &event))
-        {
-#if HAVE_MESSAGES
-          MHD_DLOG (daemon,
-                    "Call to epoll_ctl failed: %s\n",
-                    STRERROR (errno));
-#endif
-          /* and now, good luck with this... */
-        }
-      else
-        connection->epoll_state |= MHD_EPOLL_STATE_IN_EPOLL_SET;
+        event.events = EPOLLIN | EPOLLOUT | EPOLLET;
+        event.data.ptr = connection;
+        if (0 != epoll_ctl (daemon->epoll_fd,
+                            EPOLL_CTL_ADD,
+                            connection->socket_fd,
+                            &event))
+          MHD_PANIC ("Failed to add FD to epoll set\n");
+        else
+          connection->epoll_state |= MHD_EPOLL_STATE_IN_EPOLL_SET;
     }
+    connection->epoll_state &= ~MHD_EPOLL_STATE_SUSPENDED;
+  }
 #endif
   if ( (-1 != daemon->wpipe[1]) &&
        (1 != WRITE (daemon->wpipe[1], "n", 1)) )
@@ -3581,6 +3588,38 @@
           d->worker_pool_size = 0;
           d->worker_pool = NULL;
 
+          if ( (use_pipe) &&
+#ifdef WINDOWS
+               (0 != SOCKETPAIR (AF_INET, SOCK_STREAM, IPPROTO_TCP, d->wpipe))
+#else
+               (0 != PIPE (d->wpipe))
+#endif
+             )
+            {
+#if HAVE_MESSAGES
+              MHD_DLOG (daemon,
+                        "Failed to create worker control pipe: %s\n",
+                        STRERROR (errno));
+#endif
+              goto thread_failed;
+            }
+#ifndef WINDOWS
+          if ( (0 == (flags & MHD_USE_POLL)) &&
+               (1 == use_pipe) &&
+               (d->wpipe[0] >= FD_SETSIZE) )
+            {
+#if HAVE_MESSAGES
+              MHD_DLOG (daemon,
+                        "file descriptor for worker control pipe exceeds maximum value\n");
+#endif
+              if (0 != CLOSE (d->wpipe[0]))
+                MHD_PANIC ("close failed\n");
+              if (0 != CLOSE (d->wpipe[1]))
+                MHD_PANIC ("close failed\n");
+              goto thread_failed;
+            }
+#endif
+
           /* Divide available connections evenly amongst the threads.
            * Thread indexes in [0, leftover_conns) each get one of the
            * leftover connections. */
@@ -3678,6 +3717,9 @@
 
   MHD_connection_close (pos,
 			MHD_REQUEST_TERMINATED_DAEMON_SHUTDOWN);
+   if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+        (0 != pthread_mutex_lock (&daemon->cleanup_connection_mutex)) )
+     MHD_PANIC ("Failed to acquire cleanup mutex\n");
   if (pos->connection_timeout == pos->daemon->connection_timeout)
     XDLL_remove (daemon->normal_timeout_head,
 		 daemon->normal_timeout_tail,
@@ -3690,9 +3732,13 @@
 	      daemon->connections_tail,
 	      pos);
   pos->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP;
+  pos->state = MHD_CONNECTION_IN_CLEANUP;
   DLL_insert (daemon->cleanup_head,
 	      daemon->cleanup_tail,
 	      pos);
+  if ( (0 != (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) &&
+       (0 != pthread_mutex_unlock (&daemon->cleanup_connection_mutex)) )
+    MHD_PANIC ("Failed to release cleanup mutex\n");
 }
 
 
@@ -3794,10 +3840,14 @@
 	{
 	  daemon->worker_pool[i].shutdown = MHD_YES;
 	  daemon->worker_pool[i].socket_fd = -1;
+          if (-1 != daemon->wpipe[1])
+            {
+               if (1 != WRITE (daemon->worker_pool[i].wpipe[1], "e", 1))
+                 MHD_PANIC ("failed to signal shutdownn via pipe");
+            }
 #if EPOLL_SUPPORT
 	  if ( (0 != (daemon->options & MHD_USE_EPOLL_LINUX_ONLY)) &&
-	       (-1 != daemon->worker_pool[i].epoll_fd) &&
-	       (-1 == fd) )
+	       (-1 != daemon->worker_pool[i].epoll_fd) )
 	    epoll_shutdown (&daemon->worker_pool[i]);
 #endif
 	}
@@ -3839,6 +3889,13 @@
 	      MHD_PANIC ("Failed to join a thread\n");
 	  close_all_connections (&daemon->worker_pool[i]);
 	  pthread_mutex_destroy (&daemon->worker_pool[i].cleanup_connection_mutex);
+          if (-1 != daemon->wpipe[1])
+            {
+              if (0 != CLOSE (daemon->worker_pool[i].wpipe[0]))
+                MHD_PANIC ("close failed\n");
+              if (0 != CLOSE (daemon->worker_pool[i].wpipe[1]))
+                MHD_PANIC ("close failed\n");
+            }
 #if EPOLL_SUPPORT
 	  if ( (-1 != daemon->worker_pool[i].epoll_fd) &&
 	       (0 != CLOSE (daemon->worker_pool[i].epoll_fd)) )
libmicrohttpd.diff (10,545 bytes)   

Activities

Christian Grothoff

2013-11-29 12:41

manager   ~0007745

Ok, just saw this now. Parts of the patch have been applied to SVN HEAD. Not applied are 5-9, as we must not have worker-pipes per worker; instead the main signalling pipe is used differently now to ensure all works get the signal.

Issue History

Date Modified Username Field Change
2013-11-22 21:20 matt.holiday New Issue
2013-11-22 21:20 matt.holiday File Added: libmicrohttpd.diff
2013-11-29 12:41 Christian Grothoff Note Added: 0007745
2013-11-29 12:41 Christian Grothoff Status new => resolved
2013-11-29 12:41 Christian Grothoff Fixed in Version => 0.9.32
2013-11-29 12:41 Christian Grothoff Resolution open => fixed
2013-11-29 12:41 Christian Grothoff Assigned To => Christian Grothoff
2013-11-29 12:41 Christian Grothoff Product Version => Git master
2013-11-29 12:41 Christian Grothoff Target Version => 0.9.32
2013-12-04 18:54 Christian Grothoff Status resolved => closed
2024-01-21 13:24 Christian Grothoff Category libmicrohttpd API => external API