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

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



On Sun, Dec 28, 2014 at 06:49:28PM +0000, Gerry Sweeney wrote:
> 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 

Please see comments for code, in short all you need in
evthread_use_pthreads(), but you code have some design issues and also
errors that don't allow me to compile this simple program.

> 
> 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);

No such function -- httpserver_GenericHandler.

>         
>         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);

This will close accept socket, because of LEV_OPT_CLOSE_ON_FREE in
evhttp_accept_socket_with_handle(), you must use evhttp_bind_listener()
here to avoid this.

You could look into libevhtp, it has thread support out-of-the-box, and
it is simpler since it is library that just implements http server.

> 
> 	// 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);

Here you first freeing base, and then in separate thread
event_base_dispatch() will use some internal fields in it after free --
IOW you must free all resources after pthread_join().

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

Bad quotes.

>     
>     return 0;
> }
> 
> int main(int argc, char* argv[])
> {

In short all you need is add the next line here:
evthread_use_pthreads();

This will enable inter-thread notification for loop break.

[ I do have some other changes to you program, so if just adding
evthread_use_pthreads() didn't work for you -- let me know, I will send
you patch with my local changes ].

>     httpserver_start(8080, NUM_THREADS, 100);

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