test case for external event loop

This commit is contained in:
Christian Grothoff
2025-03-31 00:11:44 +02:00
parent 72e941671a
commit 9bab7100bd
4 changed files with 237 additions and 6 deletions
+6 -6
View File
@@ -4728,7 +4728,7 @@ MHD_FN_PAR_NONNULL_ALL_;
* error code otherwise
*/
#define MHD_daemon_set_option(daemon, option_ptr) \
MHD_daemon_set_options (daemon, options_ptr, 1)
MHD_daemon_set_options (daemon, option_ptr, 1)
/* *INDENT-OFF* */
@@ -6098,10 +6098,10 @@ MHD_response_from_callback (enum MHD_HTTP_StatusCode sc,
* needs to be valid while the response is used
* @param free_cb the callback to free any allocated data, called
* when response is being destroyed, can be NULL
* to skip the free/cleanup callback
* to skip the free/cleanup callback;
* @param free_cb_cls the parameter for @a free_cb
* @return NULL on error (i.e. invalid arguments, out of memory)
* FIXME: Call free callback on error?
* on error, @a free_cb is NOT called
* @ingroup response
*/
MHD_EXTERN_ struct MHD_Response *
@@ -6975,7 +6975,7 @@ MHD_upgraded_recv (struct MHD_UpgradedHandle *MHD_RESTRICT urh,
void *MHD_RESTRICT recv_buf,
size_t *MHD_RESTRICT received_size,
uint_fast64_t max_wait_millisec)
MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_SIZE_(3,2)
MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_OUT_SIZE_ (3,2)
MHD_FN_PAR_OUT_ (4);
@@ -7030,7 +7030,7 @@ MHD_upgraded_send (struct MHD_UpgradedHandle *MHD_RESTRICT urh,
size_t *MHD_RESTRICT sent_size,
uint_fast64_t max_wait_millisec,
enum MHD_Bool more_data_to_come)
MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_IN_SIZE_(3,2)
MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PAR_IN_SIZE_ (3,2)
MHD_FN_PAR_OUT_ (4);
@@ -8365,7 +8365,7 @@ MHD_response_add_auth_basic_challenge (
struct MHD_Response *MHD_RESTRICT response,
const char *MHD_RESTRICT realm,
enum MHD_Bool prefer_utf8)
MHD_FN_PAR_NONNULL_(2) MHD_FN_PAR_CSTR_ (2);
MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2);
#ifndef MHD_NO_STATIC_INLINE
+27
View File
@@ -741,6 +741,19 @@ MHDT_server_setup_minimal (const void *cls,
struct MHD_Daemon *d);
/**
* Initialize MHD daemon for an external event loop.
* Must be used together with #MHDT_server_run_external().
*
* @param cls closure (use NULL)
* @param[in,out] d daemon to initialize
* @return error message, NULL on success
*/
const char *
MHDT_server_setup_external (const void *cls,
struct MHD_Daemon *d);
/**
* Initialize MHD daemon with TLS support, binding to any free port.
*
@@ -824,6 +837,20 @@ MHDT_server_run_blocking (void *cls,
struct MHD_Daemon *d);
/**
* Function that runs an MHD daemon with an external event loop until
* a read() against @a finsig succeeds.
*
* @param cls closure
* @param finsig fd to read from to detect termination request
* @param[in,out] d daemon to run
*/
void
MHDT_server_run_external (void *cls,
int finsig,
struct MHD_Daemon *d);
/**
* Run test suite with @a phases for a daemon initialized
* using @a ss_cb on the local machine.
@@ -30,6 +30,7 @@
#include <unistd.h>
#include <errno.h>
#include <curl/curl.h>
#include <sys/epoll.h>
const char *
@@ -290,3 +291,200 @@ MHDT_server_run_blocking (void *cls,
"Failed to drain termination signal\n");
}
}
static int my_epoll_fd = -1;
/**
* The callback for registration/de-registration of the sockets to watch.
*
* This callback must not call #MHD_daemon_destroy(), #MHD_daemon_quiesce(),
* #MHD_daemon_add_connection().
*
* @param cls the closure
* @param fd the socket to watch
* @param watch_for the states of the @a fd to watch, if set to
* #MHD_FD_STATE_NONE the socket must be de-registred
* @param app_cntx_old the old application defined context for the socket,
* NULL if @a fd socket was not registered before
* @param ecb_cntx the context handle to be used
* with #MHD_daemon_event_update()
* @return NULL if error (to connection will be aborted),
* or the new socket context
* @ingroup event
*/
static void *
update_fd (
void *cls,
MHD_Socket fd,
enum MHD_FdState watch_for,
MHD_APP_SOCKET_CNTX_TYPE *app_cntx_old,
struct MHD_EventUpdateContext *ecb_cntx)
{
struct epoll_event ev;
if (watch_for == MHD_FD_STATE_NONE)
{
epoll_ctl (my_epoll_fd,
EPOLL_CTL_DEL,
fd,
&ev /* for Linux 2.6.9-compatibility */);
return NULL;
}
ev.data.ptr = ecb_cntx;
ev.events = 0;
if (0 != (watch_for & MHD_FD_STATE_RECV))
ev.events |= EPOLLIN;
if (0 != (watch_for & MHD_FD_STATE_SEND))
ev.events |= EPOLLOUT;
if (0 != (watch_for & MHD_FD_STATE_EXCEPT))
ev.events |= EPOLLRDHUP | EPOLLHUP | EPOLLERR;
if (0 !=
epoll_ctl (my_epoll_fd,
NULL == app_cntx_old
? EPOLL_CTL_ADD
: EPOLL_CTL_MOD,
fd,
&ev))
{
fprintf (stderr,
"epoll_ctl failed: %s\n",
strerror (errno));
return NULL;
}
return ecb_cntx;
}
const char *
MHDT_server_setup_external (const void *cls,
struct MHD_Daemon *d)
{
(void) cls;
if (MHD_SC_OK !=
MHD_DAEMON_SET_OPTIONS (
d,
MHD_D_OPTION_WM_EXTERNAL_EVENT_LOOP_CB_LEVEL (&update_fd,
NULL)))
return "Failed to configure external mode!";
if (MHD_SC_OK !=
MHD_DAEMON_SET_OPTIONS (
d,
MHD_D_OPTION_BIND_PORT (MHD_AF_AUTO,
0)))
return "Failed to bind to port 0!";
my_epoll_fd = epoll_create1 (0);
return NULL;
}
void
MHDT_server_run_external (void *cls,
int finsig,
struct MHD_Daemon *d)
{
fd_set r;
(void) cls; /* Unused */
if (-1 == my_epoll_fd)
abort ();
while (1)
{
uint_fast64_t next_wait;
struct timeval timeout;
if (MHD_SC_OK !=
MHD_daemon_process_reg_events (d,
&next_wait))
{
fprintf (stderr,
"MHD_daemon_process_reg_events() failed\n");
break;
}
timeout.tv_usec = next_wait;
FD_ZERO (&r);
FD_SET (finsig,
&r);
FD_SET (my_epoll_fd,
&r);
if ( (-1 ==
select ((my_epoll_fd > finsig ? my_epoll_fd : finsig) + 1,
&r,
NULL,
NULL,
&timeout)) &&
(EAGAIN != errno) )
{
fprintf (stderr,
"Failure in select(): %s\n",
strerror (errno));
break;
}
if (FD_ISSET (finsig,
&r))
break;
if (FD_ISSET (my_epoll_fd,
&r))
{
int maxevents = 40;
struct epoll_event events[maxevents];
int n;
int i;
n = epoll_wait (my_epoll_fd,
events,
maxevents,
0);
if (-1 == n)
{
fprintf (stderr,
"epoll_wait() failed: %s\n",
strerror (errno));
break;
}
for (i = 0; i < n; i++)
{
enum MHD_FdState state = MHD_FD_STATE_NONE;
if (0 != (events[i].events & EPOLLIN))
state |= MHD_FD_STATE_RECV;
if (0 != (events[i].events & EPOLLOUT))
state |= MHD_FD_STATE_SEND;
if (0 != (events[i].events & (EPOLLERR | EPOLLHUP | EPOLLRDHUP)) )
state |= MHD_FD_STATE_EXCEPT;
MHD_daemon_event_update (d,
events[i].data.ptr,
state);
}
if (MHD_SC_OK !=
MHD_daemon_process_blocking (d,
1000))
{
fprintf (stderr,
"Failure running MHD_daemon_process_blocking()\n");
break;
}
}
}
{
char c;
if ( (FD_ISSET (finsig,
&r)) &&
(1 != read (finsig,
&c,
1)) )
{
fprintf (stderr,
"Failed to drain termination signal\n");
}
}
close (my_epoll_fd);
my_epoll_fd = -1;
}
@@ -138,6 +138,12 @@ main (int argc, char *argv[])
.server_setup_cls = external0auto,
.server_runner = &MHDT_server_run_blocking,
},
{
.label = "auto-selected external event loop mode, no threads",
.server_setup = &MHDT_server_setup_external,
.server_setup_cls = NULL,
.server_runner = &MHDT_server_run_external,
},
{
.label = "END"
}