Hello everyone.
I've tried to use an external select, the suspend/resume feature and a
detached thread for some specific (slow) requests. The main idea around
this, is: the common requests (database CRUDs, html/js/css sending etc.)
are processed in the main loop within main thread (application), but, slow
requests (large report generation, remote backups etc.) are suspended to be
processed in a detached thread, which resumes the connection as soon as the
request processing ends. However, I have had some difficulty to solve
it because the MHD_queue_response() must be called by the main thread, not
by detached thread(s).
After some googling I found two good related topics:
1. https://lists.gnu.org/archive/html/libmicrohttpd/2016-09/msg0.html
2.
https://lists.endsoftwarepatents.org/archive/html/libmicrohttpd/2016-10/msg00011.html
The 1. is the same problem I'm trying to solve, but I wouldn't like to use
global variables. The 2. is exactly I'm looking for, but I couldn't find
any code/example showing how the problem was solved, so I suspect he used
the *con_cls instead of global variables to share some list reference to
the detached thread(s).
After reading these topics I got two doubts. It is a good practice to use a
shared vector (or any list + binary search) plus a mutex to manage each
detached thread? If so, could the same list be used to be iterated to call
the functions MHD_create_response_*() + MHD_queue_response() +
MHD_resume_connection() to dispatch their respective responses? I need to
take some care not to lose performance by choosing a bad design and, since
some members tried to solve the same problem, I decided to ask.
I have a draft (in attachment) which I'll use to start/try the design using
an external select + suspend/resume + pthread. It works fine if you
uncomment the macro INTERNAL_SELECT to use the internal MHD loop or the
macro TIMEOUT to use the external one. In this draft, the
MHD_queue_response() is called in the detached thread, it is wrong, but it
is just to understand how MHD works using external threads +
suspend/resume. If you keep both INTERNAL_SELECT and TIMEOUT commented, the
first request will never end unless another request arrives, I think a good
design should solve it.
I would appreciate any good idea about this. If solved, it would be nice to
convert it to a MHD example to share the solution for other members.
Thank you!
P.S.1: to test the draft above, use:
$ curl http://localhost: # simulates a common request.
$ curl http://localhost:/sleep # simulates a slow (about 10s) request.
P.S.2: I'm studying select()/epoll() and doing some exercises to fully
understand them.
--
Silvio Clécio
#include
#include
#include
#include
#include
#include
/*#define INTERNAL_SELECT 1*/
/*#define TIMEOUT 1*/
struct Request {
struct MHD_Connection *con;
char *url;
};
static void *
process_cb (void *cls)
{
struct Request *req = cls;
struct MHD_Response *res;
const union MHD_ConnectionInfo *info;
const char *msg = "hello";
if (0 == strcmp (req->url, "/sleep"))
{
usleep (1000 * 1000 * 10); /* Simulates a slow processing. */
msg = "sleep";
}
res = MHD_create_response_from_buffer (strlen (msg), (void *) msg,
MHD_RESPMEM_PERSISTENT);
MHD_queue_response (req->con, MHD_HTTP_OK, res);
MHD_resume_connection (req->con);
info = MHD_get_connection_info (req->con, MHD_CONNECTION_INFO_DAEMON);
if (NULL != info)
MHD_run (info->daemon);
MHD_destroy_response (res);
free (req->url);
free (req);
pthread_exit (NULL);
}
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;
pthread_t thrd;
(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 *) 1;
return MHD_YES;
}
*ptr = NULL;
MHD_suspend_connection (con);
req = malloc (sizeof (struct Request));
if (NULL == req)
return MHD_NO;
req->con = con;
req->url = strdup (url);
if (NULL == req->url)
{
free (req);
return MHD_NO;
}
if (0 != pthread_create (&thrd, NULL, process_cb, req))
{
free (req->url);
free (req);
return MHD_NO;
}
pthread_detach (thrd);
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_UNS