#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的行为来优雅的处理致命错误。
你需要定义一个类似event_fatal_cb签名的回调函数,然后把它传给event_set_fatal_callback。如此一来,当程序出现致命错误时,将调用你定义的回调函数。你的回调函数不能调用任何libevent函数,typedef void (*event_fatal_cb)(int err); void event_set_fatal_callback(event_fatal_cb cb);
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要用那个锁函数,在分配一个线程间共享的结构之前,你必须这么做。
所有的函数都是成功则返回0,失败则返回-1;#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
如果你需要用其他的线程库,你需要定义自己的函数来实现
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
- 读写锁。如果是进行读操作,则允许多个线程进入。但是写操作则只能有一个线程进入。写操作排斥所有读操作。
公认的锁模式:
5.调试锁使用
- EVTHREAD_READ
- EVTHREAD_WRITE
- EVTHREAD_TRY
libevent有一个可选的锁调试功能,它封装了锁调用,用来捕获典型的锁错误。包括:
释放本来没有获得的锁。
重锁一个非递归锁。
如果出现其中一种错误,libevent从失败断言退出。
6.调试事件使用void evthread_enable_lock_debugging(void); #define evthread_enable_lock_debuging() evthread_enable_lock_debugging()
使用event时libevent会向我们报告一些常见的错误。包括:
把一个未初始化的event结构体当成已经初始化来使用。
尝试重新初始化一个未定的event。
必须在创建event_base 之前调用此函数。void event_enable_debug_mode(void);
使用调试模式,当使用大量由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。
8.释放全局libevent结构#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);
即使你已经释放了所有用libevent分配的对象,但还是遗留一些全局分配的对象。这通常不是个问题,一旦程序退出,它们都将被清除。但是有时候这些对象会让一下调试工具以为内存泄漏。如果想要libevent释放所有的数据对象,可以用以下函数:
该函数不会释放任何你用libevent分配的内存。void libevent_global_shutdown(void);