[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.