设置libevent

libevent有一些全局设置是整个程序共享的,这些设置影响整个库。在调用libevent库的其他部分之前都需要对这些设置进行修改,否则libevent可能处于不一致状态。
1.日志
libevent记录内在的错误和告警,如果支持日志进行编译,它也能记录调试信息。默认情况下,这些信息输出到stderr,我们也可以提供自己的日志函数改变输出。
涉及的接口:
#define EVENT_LOG_DEBUG 0
#define EVENT_LOG_MSG   1
#define EVENT_LOG_WARN  2
#define EVENT_LOG_ERR   3

/* Deprecated; see note at the end of this section */
#define _EVENT_LOG_DEBUG EVENT_LOG_DEBUG
#define _EVENT_LOG_MSG   EVENT_LOG_MSG
#define _EVENT_LOG_WARN  EVENT_LOG_WARN
#define _EVENT_LOG_ERR   EVENT_LOG_ERR

typedefvoid (*event_log_cb)(int severity, constchar *msg);

void event_set_log_callback(event_log_cb cb);
要改变日志输出,只需要写一个跟 event_log_cb签名一样的函数,然后把它传给 event_set_log_callback即可。这样,当libevent要输出日志时,使用的就是我们写的函数,如果要恢复libevent的日志输出设置,值需要给event_set_log_callback传参数NULL就可以了。
例如
#include <event2/event.h>
#include <stdio.h>

static void discard_cb(int severity, const char *msg)
{
 /* This callback does nothing. */
}

static FILE *logfile = NULL;
static void write_to_file_cb(int severity, const char *msg)
{
    const char *s;
    if (!logfile)
        return;
    switch (severity) {
        case _EVENT_LOG_DEBUG: s = "debug"; break;
        case _EVENT_LOG_MSG:   s = "msg";   break;
        case _EVENT_LOG_WARN:  s = "warn";  break;
        case _EVENT_LOG_ERR:   s = "error"; break;
        default:               s = "?";     break; /* never reached */
    }
    fprintf(logfile, "[%s] %s\n", s, msg);
}

/* Turn off all logging from Libevent. */
void suppress_logging(void)
{
    event_set_log_callback(discard_cb);
}

/* Redirect all Libevent log messages to the C stdio file 'f'. */
void set_logfile(FILE *f)
{
    logfile = f;
    event_set_log_callback(write_to_file_cb);
}

 int main()
  {
    FILE * pFile;
    char buffer [100];

     pFile = fopen ("./myfile.txt" , "w");
      if (pFile == NULL)
          return 0;
     set_logfile(pFile);
     write_to_file_cb(_EVENT_LOG_MSG,"hello world");
     return 0;
 }
注意:
从一个用户提供的event_log_cb callback函数内调用libevent函数不是很安全。例如,你想写一个使用bufferevents 给socket发送告警信息的回调函数,那么有可能会出现一个很陌生而且很难调试的bug。这一限制在将来的版本可能会移除。
通常情况下,debug日志是不可用的,可以用以下函数手动开启。
#define EVENT_DBG_NONE 0
#define EVENT_DBG_ALL 0xffffffffu
void event_enable_debug_logging(ev_uint32_t which);
2.处理致命错误
当libevent捕获一个致命错误时,默认去调用exit()或abort()来跳出当前进程。出现这种情况时,意味着你的代码或者libevent出现bug。
通过以下函数,你可以有改变libevent的行为来优雅的处理致命错误。
typedef void (*event_fatal_cb)(int err);
void event_set_fatal_callback(event_fatal_cb cb);
你需要定义一个类似event_fatal_cb签名的回调函数,然后把它传给event_set_fatal_callback。如此一来,当程序出现致命错误时,将调用你定义的回调函数。你的回调函数不能调用任何libevent函数,
3.内存管理
默认情况下,libevent用C语言的内存管理函数从堆上分配内存。你可以为libevent提供别的内存管理器来替代C的内存管理函数。如果你有更好的内存分配器的话。
void event_set_mem_functions(void *(*malloc_fn)(size_t sz),
                             void *(*realloc_fn)(void *ptr, size_t sz),
                             void (*free_fn)(void *ptr));
例如
#include <event2/event.h>
#include <sys/types.h>
#include <stdlib.h>

/* This union's purpose is to be as big as the largest of all the * types it contains. */
union alignment {
    size_t sz;
    void *ptr;
    double dbl;
};
/* We need to make sure that everything we return is on the right alignment to hold anything, including a double. */
#define ALIGNMENT sizeof(union alignment)

/* We need to do this cast-to-char* trick on our pointers to adjust them; doing arithmetic on a void* is not standard. */
#define OUTPTR(ptr) (((char*)ptr)+ALIGNMENT)
#define INPTR(ptr) (((char*)ptr)-ALIGNMENT)

static size_t total_allocated = 0;
static void *replacement_malloc(size_t sz)
{
    void *chunk = malloc(sz + ALIGNMENT);
    if (!chunk) return chunk;
    total_allocated += sz;
    *(size_t*)chunk = sz;
    return OUTPTR(chunk);
}
static void *replacement_realloc(void *ptr, size_t sz)
{
    size_t old_size = 0;
    if (ptr) {
        ptr = INPTR(ptr);
        old_size = *(size_t*)ptr;
    }
    ptr = realloc(ptr, sz + ALIGNMENT);
    if (!ptr)
        return NULL;
    *(size_t*)ptr = sz;
    total_allocated = total_allocated - old_size + sz;
    return OUTPTR(ptr);
}
static void replacement_free(void *ptr)
{
    ptr = INPTR(ptr);
    total_allocated -= *(size_t*)ptr;
    free(ptr);
}
void start_counting_bytes(void)
{
    event_set_mem_functions(replacement_malloc,
                            replacement_realloc,
                            replacement_free);
}
注意事项:
1.必须确保在调用其他libevent函数之前进行替换。
2.你的内存分配函数返回的内存块的对齐要与C库的一样。
3.你的realloc 函数要能正确处理realloc(NULL, sz)
4..你的realloc 函数要能正确处理realloc(ptr, 0)
5.你的free函数不需要处理free(NULL)
6.你的malloc函数不需要处理malloc(0)
7.你的内存分配器有钱哦确保线程安全
8.如果你替换了libevent的内存分配函数,而且调用了libevent返回内存,你就必须用你的free函数释放内存。

4.线程和锁
libevent处理多线程的三种方法:
1.有些结构本身就是单线程的。
2.一些结构可以选择性的锁。
3.一些结构一致是锁定的。
要获得libevent锁,你必须告诉libevent要用那个锁函数,在分配一个线程间共享的结构之前,你必须这么做。
#ifdef WIN32
int evthread_use_windows_threads(void);
#define EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED
#endif
#ifdef _EVENT_HAVE_PTHREADS
int evthread_use_pthreads(void);
#define EVTHREAD_USE_PTHREADS_IMPLEMENTED
#endif
所有的函数都是成功则返回0,失败则返回-1;
如果你需要用其他的线程库,你需要定义自己的函数来实现
    
    
    
    
  • Locks

  • locking

  • unlocking

  • lock allocation

  • lock destruction

  • Conditions

  • condition variable creation

  • condition variable destruction

  • waiting on a condition variable

  • signaling/broadcasting to a condition variable

  • Threads

  • thread ID detection

并用以下函数告诉libevent
#define EVTHREAD_WRITE  0x04
#define EVTHREAD_READ   0x08
#define EVTHREAD_TRY    0x10

#define EVTHREAD_LOCKTYPE_RECURSIVE 1
#define EVTHREAD_LOCKTYPE_READWRITE 2

#define EVTHREAD_LOCK_API_VERSION 1

struct evthread_lock_callbacks {
       int lock_api_version;
       unsigned supported_locktypes;
       void *(*alloc)(unsigned locktype);
       void (*free)(void *lock, unsigned locktype);
       int (*lock)(unsigned mode, void *lock);
       int (*unlock)(unsigned mode, void *lock);
};

int evthread_set_lock_callbacks(const struct evthread_lock_callbacks *);

void evthread_set_id_callback(unsigned long (*id_fn)(void));

struct evthread_condition_callbacks {
        int condition_api_version;
        void *(*alloc_condition)(unsigned condtype);
        void (*free_condition)(void *cond);
        int (*signal_condition)(void *cond, int broadcast);
        int (*wait_condition)(void *cond, void *lock,
            const struct timeval *timeout);
};

int evthread_set_condition_callbacks(
        const struct evthread_condition_callbacks *);

公认的锁类型

0

常规的,不需要递归的锁

EVTHREAD_LOCKTYPE_RECURSIVE

递归锁。同一个线程可以多次进入,不会被阻塞。

EVTHREAD_LOCKTYPE_READWRITE
读写锁。如果是进行读操作,则允许多个线程进入。但是写操作则只能有一个线程进入。写操作排斥所有读操作。

公认的锁模式:

EVTHREAD_READ

EVTHREAD_WRITE

EVTHREAD_TRY

5.调试锁使用
libevent有一个可选的锁调试功能,它封装了锁调用,用来捕获典型的锁错误。包括:
释放本来没有获得的锁。
重锁一个非递归锁。
如果出现其中一种错误,libevent从失败断言退出。
void evthread_enable_lock_debugging(void);
#define evthread_enable_lock_debuging() evthread_enable_lock_debugging()
6.调试事件使用
使用event时libevent会向我们报告一些常见的错误。包括:
把一个未初始化的event结构体当成已经初始化来使用。
尝试重新初始化一个未定的event。
void event_enable_debug_mode(void);
必须在创建event_base 之前调用此函数。
使用调试模式,当使用大量由event_assign()创建的event时程序内存可能耗尽,因为libevent没有办法知道哪个event不再需要。要避免出现这种内存耗尽的情况,你必须显示的告诉libevent哪个event不再需要。
void event_debug_unassign(struct event *ev);
如果不是调试模式,此函数不起任何作用。
#include <event2/event.h>
#include <event2/event_struct.h>

#include <stdlib.h>

void cb(evutil_socket_t fd, short what, void *ptr)
{
 /* We pass 'NULL' as the callback pointer for the heap allocated * event, and we pass the event itself as the callback pointer * for the stack-allocated event. */
    struct event *ev = ptr;

    if (ev)
        event_debug_unassign(ev);
}

/* Here's a simple mainloop that waits until fd1 and fd2 are both * ready to read. */
void mainloop(evutil_socket_t fd1, evutil_socket_t fd2, int debug_mode)
{
    struct event_base *base;
    struct event event_on_stack, *event_on_heap;

    if (debug_mode)
       event_enable_debug_mode();

    base = event_base_new();

    event_on_heap = event_new(base, fd1, EV_READ, cb, NULL);
    event_assign(&event_on_stack, base, fd2, EV_READ, cb, &event_on_stack);

    event_add(event_on_heap, NULL);
    event_add(&event_on_stack, NULL);

    event_base_dispatch(base);

    event_free(event_on_heap);
    event_base_free(base);
}

7.检测libevent版本
有时候我们需要检测libevent版本,以便于:
1.已经安装的libevent版本对于我们程序来说足够好。
2.调试时显示libevent版本
3.检测libevent,并警告用户相关bugs。
#define LIBEVENT_VERSION_NUMBER 0x02000300
#define LIBEVENT_VERSION "2.0.3-alpha"
const char *event_get_version(void);
ev_uint32_t event_get_version_number(void);
8.释放全局libevent结构
即使你已经释放了所有用libevent分配的对象,但还是遗留一些全局分配的对象。这通常不是个问题,一旦程序退出,它们都将被清除。但是有时候这些对象会让一下调试工具以为内存泄漏。如果想要libevent释放所有的数据对象,可以用以下函数:
void libevent_global_shutdown(void);
该函数不会释放任何你用libevent分配的内存。













你可能感兴趣的:(libevent)