Hello,
You are right. But there is an issue (at least on Linux). It works with
"MHD_USE_EPOLL_LINUX_ONLY" - but not with "select()"/"poll()".
Sooner or later the daemon gets stalled. Maybe a race condition?
I have attached the patched testcase.
Thanks a lot!
Markus
Am Donnerstag, den 14.04.2016, 17:35 +0300 schrieb Evgeny Grin:
> Markus,
>
> You have resources leak in your test.
> Modify with
> ===================
> --- test.cpp.orig 2016-04-14 16:38:27.000000000 +0300
> +++ test.cpp 2016-04-14 17:26:17.677237034 +0300
> @@ -24,7 +24,9 @@ static void suspend_connection( struct M
> pthread_t thread_id;
> pthread_attr_t thread_attr;
> pthread_attr_init( &thread_attr );
> + pthread_attr_setdetachstate(&thread_attr,
> PTHREAD_CREATE_DETACHED);
> pthread_create( &thread_id, &thread_attr, resume_connection,
> connection );
> + pthread_attr_destroy(&thread_attr);
> }
> ===================
> And it will work with current and previous versions of libmicrohttpd.
> Otherwise you just run out of system resources.
> Good that libmicrohttpd didn't crash even in this case. :)
>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <microhttpd.h>
static int request_counter = 0;
static void* resume_connection( void *arg )
{
struct MHD_Connection *connection = (struct MHD_Connection *)arg;
MHD_resume_connection( connection );
return NULL;
}
static void suspend_connection( struct MHD_Connection *connection )
{
MHD_suspend_connection( connection );
// wait to resume
pthread_t thread_id;
pthread_attr_t thread_attr;
pthread_attr_init( &thread_attr );
pthread_attr_setdetachstate( &thread_attr, PTHREAD_CREATE_DETACHED );
int status = pthread_create( &thread_id, &thread_attr, resume_connection, connection );
if( status != 0 )
{
fprintf( stderr, "count not clone thead\n" );
exit( EXIT_FAILURE );
}
pthread_attr_destroy( &thread_attr );
}
struct ContentReaderUserdata
{
int bytes_written;
struct MHD_Connection *connection;
};
static ssize_t http_ContentReaderCallback( void *cls, uint64_t pos, char *buf, size_t max )
{
struct ContentReaderUserdata *userdata = (struct ContentReaderUserdata*)cls;
if( userdata->bytes_written >= 1024 )
{
fprintf( stderr, "finish: %d\n", request_counter );
return MHD_CONTENT_READER_END_OF_STREAM;
}
userdata->bytes_written += 1;
// write a byte
const char alphabet[] = "\nABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
buf[0] = alphabet[ userdata->bytes_written % (sizeof(alphabet) - 1) ];
// Suspend connection
suspend_connection( userdata->connection );
return 1;
}
static int http_AccessHandlerCallback( void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls )
{
// Never respond on first call
if( *con_cls == NULL )
{
fprintf( stderr, "start: %d\n", ++request_counter );
struct ContentReaderUserdata *userdata = (struct ContentReaderUserdata*) malloc( sizeof(ContentReaderUserdata) );
userdata->bytes_written = 0;
userdata->connection = connection;
*con_cls = userdata;
return MHD_YES;
}
// Second call: create response
struct MHD_Response *response = MHD_create_response_from_callback( -1, 32*1024, http_ContentReaderCallback, *con_cls, NULL );
MHD_queue_response( connection, MHD_HTTP_OK, response );
MHD_destroy_response( response );
suspend_connection( connection );
return MHD_YES;
}
int main()
{
// create a socket on port 8080
int ai_family = AF_INET;
struct sockaddr addr_socket;
struct sockaddr_in *in_addr = (struct sockaddr_in *) &addr_socket;
memset( in_addr, 0, sizeof(*in_addr) );
in_addr->sin_family = AF_INET;
in_addr->sin_port = htons( 8080 );
in_addr->sin_addr.s_addr = htonl( INADDR_ANY );
socklen_t addr_len = sizeof(*in_addr);
// Create socket
int listening_socket = socket( ai_family, SOCK_STREAM, IPPROTO_TCP );
if( listening_socket == -1 )
{
fprintf( stderr, "Could not create socket: %s.\n", strerror(errno) );
return EXIT_FAILURE;
}
// Non blocking socket
int file_descriptor_flag = fcntl( listening_socket, F_GETFL );
fcntl( listening_socket, F_SETFL, file_descriptor_flag | O_NONBLOCK );
// Socket options
int optval = 1;
setsockopt( listening_socket, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval) );
// Bind socket
int bound = bind( listening_socket, &addr_socket, addr_len );
if( bound == -1 )
{
close( listening_socket );
fprintf( stderr, "Bind failed: %s\n", strerror(errno) );
return EXIT_FAILURE;
}
// Start listening
int listening = listen( listening_socket, 31 );
if( listening == -1 )
{
close( listening_socket );
fprintf( stderr, "Listen failed: %s\n", strerror(errno) );
return EXIT_FAILURE;
}
// Create daemon
unsigned int daemon_flags = MHD_USE_SELECT_INTERNALLY | MHD_USE_SUSPEND_RESUME | MHD_USE_PIPE_FOR_SHUTDOWN | MHD_USE_DEBUG;
struct MHD_OptionItem options[] =
{
{ MHD_OPTION_LISTEN_SOCKET, listening_socket, NULL },
{ MHD_OPTION_THREAD_POOL_SIZE, 1, NULL },
{ MHD_OPTION_END, 0, NULL }
};
struct MHD_Daemon *daemon = MHD_start_daemon( daemon_flags, 0,
NULL, NULL,
http_AccessHandlerCallback, NULL,
MHD_OPTION_ARRAY, options,
MHD_OPTION_END );
if( daemon == NULL )
return EXIT_FAILURE;
// forever
sleep( 999999999 );
// Stop daemon
MHD_stop_daemon( daemon );
return EXIT_SUCCESS;
}