View Issue Details
| ID | Project | Category | View Status | Date Submitted | Last Update |
|---|---|---|---|---|---|
| 0005661 | libmicrohttpd | external even loop | public | 2019-03-22 07:52 | 2019-08-01 14:38 |
| Reporter | silvioprog | Assigned To | Christian Grothoff | ||
| Priority | normal | Severity | block | Reproducibility | always |
| Status | closed | Resolution | no change required | ||
| Platform | GNU/Linux | OS | Xubuntu | OS Version | 18.10 |
| Product Version | Git master | ||||
| Target Version | 0.9.64 | Fixed in Version | 0.9.64 | ||
| Summary | 0005661: Request is never finished when using suspend/resume combined with external select and pthreads | ||||
| Description | Hello. I've tried to process a slow request in a detached thread combined with suspend/resume and external select, but the request is never finished. | ||||
| Steps To Reproduce | 1. Build and run the example below. 2. In a terminal tab, perform: $ curl -w '\n' http://localhost:8080/sleep 3. In other terminal tab, perform (multiple times): $ curl -w '\n' http://localhost:8080 This first test will work fine, because it is using the internal MHD select. Now, enabled the external select by changing the line 7 from "#define INTERNAL_SELECT 1" to "/* #define INTERNAL_SELECT 1 */" and build/run the example again; repeat the steps 2. and 3. and notice the 2. will stall.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <microhttpd.h>
#define INTERNAL_SELECT 1
#ifndef INTERNAL_SELECT
#include <errno.h>
#endif
#define HELLO_CONTENT "<html><body>hello</body></html>"
#define SLEEP_CONTENT "<html><body>sleep</body></html>"
struct Request {
struct MHD_Connection *con;
size_t written;
size_t total;
};
static void *thr_cb (void *arg)
{
usleep (1000 * 100); /* Just to simulate a slow process ... */
MHD_resume_connection (arg);
pthread_exit (NULL);
}
static ssize_t crc_cb (void *cls,
uint64_t pos,
char *buf,
size_t max)
{
struct Request *req = cls;
pthread_t thr;
(void) pos; /* Unused. Silence compiler warning. */
(void) max; /* Unused. Silence compiler warning. */
if (req->written >= req->total)
{
return MHD_CONTENT_READER_END_OF_STREAM;
}
*buf = SLEEP_CONTENT[req->written++];
MHD_suspend_connection (req->con);
if (0 != pthread_create (&thr, NULL, thr_cb, req->con))
return MHD_NO;
pthread_detach (thr);
return MHD_YES;
}
static int ahc_cb (void *cls,
struct MHD_Connection *con,
const char *url,
const char *method,
const char *version,
const char *upload_data,
size_t *upload_data_size,
void **ptr)
{
struct Request *req;
struct MHD_Response *res;
(void) cls; /* Unused. Silence compiler warning. */
(void) method; /* Unused. Silence compiler warning. */
(void) version; /* Unused. Silence compiler warning. */
(void) upload_data; /* Unused. Silence compiler warning. */
(void) upload_data_size; /* Unused. Silence compiler warning. */
if (NULL == *ptr)
{
*ptr = (void *) MHD_YES;
return MHD_YES;
}
*ptr = NULL;
if (0 == strcmp (url, "/sleep"))
{
req = malloc (sizeof (struct Request));
if (NULL == req)
return MHD_NO;
req->con = con;
req->written = 0;
req->total = strlen (SLEEP_CONTENT);
res = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, 4096,
&crc_cb,
req,
&free);
}
else
{
res = MHD_create_response_from_buffer (strlen (HELLO_CONTENT),
(void *) HELLO_CONTENT,
MHD_RESPMEM_PERSISTENT);
}
MHD_queue_response (con, MHD_HTTP_OK, res);
MHD_destroy_response (res);
return MHD_YES;
}
int main (int argc,
char *const *argv)
{
struct MHD_Daemon *d;
unsigned int flags = MHD_USE_SUSPEND_RESUME | MHD_USE_ERROR_LOG;
#ifndef INTERNAL_SELECT
struct timeval tv;
struct timeval *tvp;
fd_set rs;
fd_set ws;
fd_set es;
MHD_socket max;
MHD_UNSIGNED_LONG_LONG timeout;
int errnum;
if (argc != 2)
{
printf ("%s PORT\n", argv[0]);
return 1;
}
#else
flags += MHD_USE_AUTO_INTERNAL_THREAD;
#endif
d = MHD_start_daemon (flags,
(uint16_t) atoi (argv[1]),
NULL, NULL,
&ahc_cb, NULL,
MHD_OPTION_END);
if (NULL == d)
return 1;
#ifdef INTERNAL_SELECT
(void) getc (stdin);
#else
while (1)
{
max = 0;
FD_ZERO(&rs);
FD_ZERO(&ws);
FD_ZERO(&es);
timeout = (MHD_UNSIGNED_LONG_LONG) -1;
if (MHD_YES != MHD_get_fdset(d, &rs, &ws, &es, &max))
break;
if (MHD_YES == MHD_get_timeout (d, &timeout))
{
tv.tv_sec = (time_t) (timeout / 1000L);
tv.tv_usec = (suseconds_t) ((timeout % 1000L) * 1000L);
tvp = &tv;
}
else
{
tvp = NULL;
}
if (-1 == select (max + 1, &rs, &ws, &es, tvp))
{
errnum = errno;
if (EINTR != errnum)
{
fprintf (stderr, "Aborting due to error during select: %s\n", strerror (errnum));
fflush (stderr);
}
break;
}
if (!MHD_run_from_select (d, &rs, &ws, &es))
return 1;
}
#endif
MHD_stop_daemon (d);
return 0;
}
| ||||
| Additional Information | Not sure if it is indeed a bug, but I've tried to fix it around a week but no success. Related to (but using other example): http://lists.gnu.org/archive/html/libmicrohttpd/2019-03/msg00004.html. | ||||
| Tags | No tags attached. | ||||
|
|
If you are using the 'external' select, you are responsible for 'kicking' the external select loop back into action when you call MHD_connection_resume(). So you should (for example) add an additional eventfd() file descriptor to the &rs, drain it inside select() and write to it at the time where you call MHD_connection_resume(). Otherwise your main() function will be blocked in select() and MHD has no chance of executing the resume. |
|
|
This is exactly I'm looking for. Thanks a lot for explaining and showing the way to follow. After your explanation I took a look at MHD sources and found an entry for "sys/eventfd.h". Now that I know where to go I'm going to study it to return some feedback. :-) I've noticed some members looking for it too, so I'll convert it to a MHD example as soon as it is done. I've used IPC before inside NodeJS(JS) and Delphi(Pascal), it is the first time I'll use it in C in a more raw way. Having success I will close this issue followed by the commit with the example. Thanks again for the help you have given me all these years. :-D |
| Date Modified | Username | Field | Change |
|---|---|---|---|
| 2019-03-22 07:52 | silvioprog | New Issue | |
| 2019-03-22 07:57 | silvioprog | Steps to Reproduce Updated | |
| 2019-03-22 10:01 | Christian Grothoff | Note Added: 0014234 | |
| 2019-03-22 23:16 | silvioprog | Note Added: 0014235 | |
| 2019-04-10 13:26 | Christian Grothoff | Assigned To | => Christian Grothoff |
| 2019-04-10 13:26 | Christian Grothoff | Status | new => resolved |
| 2019-04-10 13:26 | Christian Grothoff | Resolution | open => no change required |
| 2019-04-10 13:26 | Christian Grothoff | Fixed in Version | => 0.9.64 |
| 2019-04-10 13:26 | Christian Grothoff | Target Version | Git master => 0.9.64 |
| 2019-08-01 14:38 | Christian Grothoff | Status | resolved => closed |
| 2024-01-21 13:24 | Christian Grothoff | Category | libmicrohttpd external select => external even loop |