11

The main function is based on libevent, but there is a long run task in the function. So start N treads to run the tasks. Is is this idea OK? And how to use libevent and pthread together in C?

Bruce Dou
  • 4,673
  • 10
  • 37
  • 56
  • This is easy enough to do with processes, if you look at OpenBSD's relayd, there's code for multiple processes handling incoming requests. – tbert Jun 20 '12 at 11:14

4 Answers4

3

Bumping an old question, may have already been solved. But posting the answer just in case someone else needs it.

Yes, it is okay to do threading in this case. I recently used libevent in pthreads, and it seems to be working just fine. Here's the code :

#include <stdint.h>
#include <pthread.h>
#include <event.h>

void * thread_func (void *);

int main(void)
{
    int32_t tid = 0, ret = -1;
    struct event_base *evbase;
    struct event *timer;
    int32_t *t_ret = &ret;
    struct timeval tv;

    // 1. initialize libevent for pthreads
    evthread_use_pthreads();

    ret = pthread_create(&tid, NULL, thread_func, NULL);
    // check ret for error

    // 2. allocate event base
    evbase = event_base_new();
    // 3. allocate event object
    timer = event_new(evbase, -1, EV_PERSIST, callback_func, NULL);
    // 4. add event
    tv.tv_sec = 0;
    tv.tv_usec = 1000;
    evtimer_add(timer, &tv);
    // 5. start the event loop
    event_base_dispatch(evbase);   // event loop

    // join pthread...

    // 6. free resources
    event_free(timer);
    event_base_free(evbase);
    return 0;
}

void * thread_func(void *arg)
{
    struct event *ev;
    struct event_base *base;

    base = event_base_new();
    ev = event_new(base, -1, EV_PERSIST, thread_callback, NULL);
    event_add(ev, NULL);  // wait forever
    event_base_dispatch(base);  // start event loop

    event_free(ev);
    event_base_free(base);
    pthread_exit(0);
}

As you can see, in my case, the event for the main thread is timer. The base logic followed is as below :

  1. call evthread_use_pthreads() to initialize libevent for pthreads on Linux (my case). For windows evthread_use_window_threads(). Check out the documentation given in event.h itself.
  2. Allocate an event_base structure on global heap as instructed in documentation. Make sure to check return value for errors.
  3. Same as above, but allocate event structure itself. In my case, I am not waiting on any file descriptor, so -1 is passed as argument. Also, I want my event to persist, hence EV_PERSIST . The code for callback functions is omitted.
  4. Schedule the event for execution
  5. Start the event loop
  6. free the resources when done.

Libevent version used in my case is libevent2 5.1.9 , and you will also need libevent_pthreads.so library for linking.

cheers.

aditya
  • 952
  • 3
  • 16
  • 33
1

That would work.

In the I/O callback function delegates time consuming job to another thread of a thread pool. The exact mechanics depend on the interface of the worker thread or the thread pool.

To communicate the result back from the worker thread to the I/O thread use a pipe. The worker thread writes the pointer to the result object to the pipe and the I/O thread wakes up and read the pointer from the pipe.

Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
1

There is a multithreaded libevent example in this blog post: http://www.roncemer.com/multi-threaded-libevent-server-example

His solution is, to quote:

The solution is to create one libevent event queue (AKA event_base) per active connection, each with its own event pump thread. This project does exactly that, giving you everything you need to write high-performance, multi-threaded, libevent-based socket servers.

ahcox
  • 9,349
  • 5
  • 33
  • 38
  • Broken link or maybe the server is down. Please copy and paste code in addition to URLs rather than providing just the links. – enthusiasticgeek Feb 07 '13 at 15:01
  • 1
    @enthusiasticgeek, The server is up and a search on his blog shows no posts on libevent, so maybe he found a flaw and just deleted the post. Here is a snapshot of the page from 2012: http://web.archive.org/web/20120707080222/http://www.roncemer.com/multi-threaded-libevent-server-example Here is the sourceforge project linked-to from the snapshot: http://sourceforge.net/projects/libevent-thread/ I haven't looked at this for a while but you are welcome to cut and paste some code yourself if you have your head in the problem right now. – ahcox Feb 09 '13 at 01:53
0

NOTE This is for libev not libevent but the idea may apply.

Here I present an example for the community. Please comment and let me know if there are any noticable bugs. This example could include a signal handler for thread termination and graceful exit in the future.

//This program is demo for using pthreads with libev. 
//Try using Timeout values as large as 1.0 and as small as 0.000001
//and notice the difference in the output

//(c) 2009 debuguo
//(c) 2013 enthusiasticgeek for stack overflow
//Free to distribute and improve the code. Leave credits intact
//compile using:           gcc -g test.c -o test -lpthread -lev

#include <ev.h>
#include <stdio.h> // for puts
#include <stdlib.h>
#include <pthread.h>

pthread_mutex_t lock;
double timeout = 0.00001;
ev_timer timeout_watcher;
int timeout_count = 0;

ev_async async_watcher;
int async_count = 0;

struct ev_loop* loop2;

void* loop2thread(void* args)
{ 
    // now wait for events to arrive on the inner loop
    ev_loop(loop2, 0);
    return NULL;
}

static void async_cb (EV_P_ ev_async *w, int revents)
{
    //puts ("async ready");
    pthread_mutex_lock(&lock);     //Don't forget locking
    ++async_count;
    printf("async = %d, timeout = %d \n", async_count, timeout_count);
    pthread_mutex_unlock(&lock);   //Don't forget unlocking
}

static void timeout_cb (EV_P_ ev_timer *w, int revents) // Timer callback function
{
    //puts ("timeout");
    if(ev_async_pending(&async_watcher)==false){ //the event has not yet been processed (or even noted) by the event loop? (i.e. Is it serviced? If yes then proceed to)
        ev_async_send(loop2, &async_watcher); //Sends/signals/activates the given ev_async watcher, that is, feeds an EV_ASYNC event on the watcher into the event loop. 
    }

    pthread_mutex_lock(&lock);     //Don't forget locking
    ++timeout_count;
    pthread_mutex_unlock(&lock);   //Don't forget unlocking
    w->repeat = timeout;
    ev_timer_again(loop, &timeout_watcher); //Start the timer again.
}

int main (int argc, char** argv)
{
    if (argc < 2) {
        puts("Timeout value missing.\n./demo <timeout>");
        return -1;
    }
    timeout = atof(argv[1]);

    struct ev_loop *loop = EV_DEFAULT;  //or ev_default_loop (0);

    //Initialize pthread
    pthread_mutex_init(&lock, NULL);
    pthread_t thread;

    // This loop sits in the pthread
    loop2 = ev_loop_new(0);

    //This block is specifically used pre-empting thread (i.e. temporary interruption and suspension of a task, without asking for its cooperation, with the intention to resume that task later.)  
    //This takes into account thread safety
    ev_async_init(&async_watcher, async_cb);
    ev_async_start(loop2, &async_watcher);
    pthread_create(&thread, NULL, loop2thread, NULL);

    ev_timer_init (&timeout_watcher, timeout_cb, timeout, 0.); // Non repeating timer. The timer starts repeating in the timeout callback function
    ev_timer_start (loop, &timeout_watcher);

    // now wait for events to arrive on the main loop
    ev_loop(loop, 0);
    //Wait on threads for execution
    pthread_join(thread, NULL);

    pthread_mutex_destroy(&lock);
    return 0;
}
enthusiasticgeek
  • 2,640
  • 46
  • 53