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 |