[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

Re: [Libevent-users] Help with Libevent multi-threaded HTTP server



Hi Azat,

See below, this is the program, see the comments in main() for an explanation of the problem. This needs to be a c++ compile as I am using an STL <vector>.  I have made it work by freeing the event_base object by calling free_event_base() but that feels like the wrong thing to do. 

Thanks for the response, I appreciate itâany help would be much appreciated 

Gerry

----------------------------------
#include <sys/types.h>
#include <sys/time.h>
#include <sys/queue.h>
#include <stdlib.h>
#include <unistd.h>
#include <err.h>
#include <event.h>
#include <evhttp.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <iostream>
#include <vector>

void httpserver_ProcessRequest(struct evhttp_request *req) {
    struct evbuffer *buf = evbuffer_new();
    if (buf == NULL)
        return;
    
    evbuffer_add_printf(buf, "Requested: %s", evhttp_request_uri(req));
    
    evhttp_send_reply(req, HTTP_OK, "OK", buf);

    evbuffer_free(buf);
}

void* httpserver_Dispatch(void *arg) {
    
    event_base_dispatch((struct event_base*)arg);
    
    std::cout << "Event dispatch thread ended..." << std::endl;
    
    return 0;
}

int httpserver_bindsocket(int port, int backlog)
{
    int r;
    int nfd;
    nfd = socket(AF_INET, SOCK_STREAM, 0);
    if (nfd < 0) return -1;
    
    int one = 1;
    r = setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(int));
    
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(port);
    
    r = bind(nfd, (struct sockaddr*)&addr, sizeof(addr));
    if (r < 0) return -1;
    r = listen(nfd, backlog);
    if (r < 0) return -1;
    
    int flags;
    if ((flags = fcntl(nfd, F_GETFL, 0)) < 0
        || fcntl(nfd, F_SETFL, flags | O_NONBLOCK) < 0)
        return -1;
    
    return nfd;
}

int httpserver_start(int port, int nthreads, int backlog) 
{
    int r, i;
    int nfd = httpserver_bindsocket(port, backlog);
    if (nfd < 0)
        return -1;
    
    struct workder_t
    {
        struct event_base* base;
        struct evhttp* httpd;
        struct evhttp_bound_socket* soc;
        pthread_t thr;
    };
    
    std::vector<workder_t> _workers;
    
    for (i = 0; i < nthreads; i++)
    {
        workder_t worker;
        
        worker.base = event_base_new();
        if (worker.base == NULL)
            return -1;
        
        worker.httpd = evhttp_new(worker.base);
        if (worker.httpd == NULL)
            return -1;
        
        worker.soc = evhttp_accept_socket_with_handle(worker.httpd, nfd);
        
        evhttp_set_gencb(worker.httpd, httpserver_GenericHandler, NULL);
        
        r = pthread_create(&worker.thr, NULL, httpserver_Dispatch, worker.base);
        
        if (r != 0)
            return -1;
        
        _workers.push_back(worker);
    }
    
    std::cout << "Running - press return to stop..." << std::endl;
    getchar();
    std::cout << "Stopping..." << std::endl;
    
    close(nfd);
    std::cout << "Closed listening socket..." << std::endl;
    
    for(auto worker = _workers.begin(); worker != _workers.end(); ++worker)
    {
	// I would expect that this would cause the thread blocked in event_base_dispatch() function called in  httpserver_Dispatch function to end
        event_base_loopbreak(worker->base);
        
	    // seems like I should do this, but not sureâworks either way
        evhttp_free(worker->httpd);

	// If I do this the the event_base_dispatch() does return so I get the expected result, but this feels wrong to 
	    // destroy the base before the thread ends
            // comment the next line to see the problem
        event_base_free(worker->base);

	// We are in the main thread so we join with each worker thread here
        pthread_join(worker->thr, NULL);
    }
    std::cout << âClosed..." << std::endl;
    
    return 0;
}

int main(int argc, char* argv[])
{
    httpserver_start(8080, NUM_THREADS, 100);
}  


Gerry Sweeney
http://gerrysweeney.com/


> On 28 Dec 2014, at 18:13, Azat Khuzhin <a3at.mail@xxxxxxxxx> wrote:
> 
> On Sun, Dec 28, 2014 at 01:30:36PM +0000, Gerry Sweeney wrote:
>> Hi,
>> 
>> I am new to lib event so please forgive me if this is a noob question. I ran into a problem usinglib event and wonder if anyone can help
>> 
>> My question I hope is very simple: -
>> 
>> I am trying to create a simple multi-threaded HTTP server.  
>> 
>> I create and bind a non-blocking listening socket on port 8080, then for each thread I want to create to process HTTP requests I call the following
>> 
>> event_base_new()
>> evhttp_new()
>> evhttp_accept_socket_with_handle()
>> evhttp_set_gencb()
>> pthread_create(my_http_worker_thread_fn, base);
>> 
>> Then in the thread function for each worker thread I call event_base_dispatch((struct event_base*)arg);
>> 
>> Everything works as expected and I can process HTTP requests. The problem I am having is shutting down cleanly. 
>> 
>> After my main thread creates the worker threads I wait for a return key press on getchar() so I can press return to shutdown, after which I call event_base_loopbreak() on each event base I created, then I call pthread_join() from my main thread on each worker thread handle to wait for all threads to complete. 
>> 
>> The problem is, after calling event_base_loopbreak() I am expecting event_base_dispatch() to return but it never does, looking at the call stack for each thread they all appear to be waiting for an event. So I end up stuck waiting for threads to end which never do.
> 
> The loop must breaks after event_base_loopbreak().
> 
> Could you try debug mode and analyze it's messages?
> If you could write 50L code sample to reproduce this problem - I
> could have a look.
> 
> Cheers,
> Azat.
> ***********************************************************************
> To unsubscribe, send an e-mail to majordomo@xxxxxxxxxxxxx with
> unsubscribe libevent-users    in the body.

***********************************************************************
To unsubscribe, send an e-mail to majordomo@xxxxxxxxxxxxx with
unsubscribe libevent-users    in the body.