libevent学习笔记 -1 设置 Libevent库

0 Preliminaries
1 设置 Libevent库
2 event_base

Libevent有一些整个库共享的全局设置,在调用库的任一部分之前都需要修改这些设置,否则有可能会出现不一致的状态。

  • log日志

Libevent能够打印内部错误和警告信息,默认情况下这些信息都是通过标准输出,所以我们也可以复写我们自己的log函数。接口如下:

#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

typedef void (*event_log_cb)(int severity, const char *msg);

void event_set_log_callback(event_log_cb cb);

想要重写Libevent的log,只需要写一个上面的函数作为参数传入event_set_log_callback(),当Libevent要打印log时会将信息穿给我们自己写的函数,可以向event_set_log_callback()传入NULL使log以默认方式输出。看下面一个例子:

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

注意:在调用者自己写的event_log_cb callback函数里面调用Libevent函数是不安全的。比如在回调函数里面使用bufferevents向网络socket写警告信息,很有可能遇到奇怪而又难以诊断的问题,这个局限性问题在未来版本的Libevent中可能会被解决。默认debug logs不可用,可手动开启:

#define EVENT_DBG_NONE 0
#define EVENT_DBG_ALL 0xffffffffu

void event_enable_debug_logging(ev_uint32_t which);

调用event_enable_debug_logging() 传入EVENT_DBG_NONE为默认设置,传入EVENT_DBG_ALL是开始所有log,以后的版本可能会支持更多选项。这些功能函数声明在“event2/event.h”。

  • 处理致命错误

当Libevent感知到不可恢复的内部错误时,默认执行是调用exit() 或 abort()函数退出当前运行程序,因为这些错误意味着在你写的程序中或者Libevent库中存在bug。我们还可以像下面一样重写callback函数来处理致命错误:

typedef void (*event_fatal_cb)(int err);
void event_set_fatal_callback(event_fatal_cb cb);

上面的函数同样声明在“event2/event.h”,Libevent 2.0.3-alpha以后才有。需要注意的是,在我们自定义的callback调用函数里,不能调用Libevent的任何函数。

  • 内存管理

默认情况,Libevent使用C内存管理方法从堆中分配内存,Libevent提供替换malloc, realloc, 和free的接口,这样你就可以设置自己想要使用的内存管理策略。接口如下所示,声明在“event2/event.h”, 第一次出现在Libevent 2.0.1-alpha.

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);
}
  • 加锁线程

我们知道,在多线程程序中多线程访问临界资源时需要加锁,Libevent结构体具有三种多线程工作方式:(1)本质上的单线程结构;(2)可选择加锁结构;(3)持续加锁结构;
想要实现加锁,需要告诉Libevent使用的加锁函数,如果是使用线程库或者本地windows线程代码,那就很方便了,因为库中的预定义会自动选择:

#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的evthread_set_lock_callbacks和evthread_set_id_callback接口:

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

结构体evthread_lock_callbacks定义了加锁的callback函数和他们的功能,alloc函数返回特定类型的新锁;free函数释放特定类型锁持有的所有资源;lock函数申请对特定类型加锁,unlock函数解锁,这两个函数都是返回0值代表加锁成功,非零失败;对于上面这些函数的使用例子,可以参考文件evthread_pthread.c和evthread_win32.c。

  • 查看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);

例子:Compile-time

#include <event2/event.h>

#if !defined(LIBEVENT_VERSION_NUMBER) || LIBEVENT_VERSION_NUMBER < 0x02000100
#error "This version of Libevent is not supported; Get 2.0.1-alpha or later."
#endif

int
make_sandwich(void)
{
        /* Let's suppose that Libevent 6.0.5 introduces a make-me-a
           sandwich function. */
#if LIBEVENT_VERSION_NUMBER >= 0x06000500
        evutil_make_me_a_sandwich();
        return 0;
#else
        return -1;
#endif
}

例子: Run-time

#include <event2/event.h>
#include <string.h>

int
check_for_old_version(void)
{
    const char *v = event_get_version();
    /* This is a dumb way to do it, but it is the only thing that works
       before Libevent 2.0. */
    if (!strncmp(v, "0.", 2) ||
        !strncmp(v, "1.1", 3) ||
        !strncmp(v, "1.2", 3) ||
        !strncmp(v, "1.3", 3)) {

        printf("Your version of Libevent is very old.  If you run into bugs,"
               " consider upgrading.\n");
        return -1;
    } else {
        printf("Running with Libevent version %s\n", v);
        return 0;
    }
}

int
check_version_match(void)
{
    ev_uint32_t v_compile, v_run;
    v_compile = LIBEVENT_VERSION_NUMBER;
    v_run = event_get_version_number();
    if ((v_compile & 0xffff0000) != (v_run & 0xffff0000)) {
        printf("Running with a Libevent version (%s) very different from the "
               "one we were built with (%s).\n", event_get_version(),
               LIBEVENT_VERSION);
        return -1;
    }
    return 0;
}

event_get_version()第一次出现在版本Libevent 1.0c,其他函数第一次出现在Libevent 2.0.1-alpha。

  • 释放全局结构体

尽管已经释放了Libevent分配的所有对象,还是会有一些分配的全局结构体存在,虽然程序退出时它们也会被回收,但是对于一些调试工具而言会被误导为是Libevent库的弱链接资源,调用下面的接口函数可主动释放所有的全局结构体对象:

void libevent_global_shutdown(void);
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值