一起读读libevent的源代码:Libevent 第一章 设置libevent (1)

某人曾提醒我要多读源代码,我就选了libevent 2.1.8稳定版的源代码来读。

读了一会,纯看源代码里面的东西,还挺无聊的。所以我就开始,便看他们的编程教程:

http://www.wangafu.net/~nickm/libevent-book/

然后每遇到实现,我就跑去源代码中看别人怎么做到的。

这样还是比较有趣的,一个一个小目标的去做,直到这个事情是为什么而做。

我之前,已经把编程的指导粗略看过一边,也是边犯困边看,再开始看源代码,昨天睡了十次八次,才看了很小的一部分。

这样太慢了,而且很无聊。所以换了这样的方式,省去细枝末节,按功能来进行逐个翻看。

下面是我个人做这个事的笔记   

ps:这里面前面代码里面有数字,是代码的数字,可以参考我之前的设置LXR来查看这些源代码,会比较方便。


如何设置log messag:

可以通过以下的接口:

Interface
#define EVENT_LOG_DEBUG 0
#define EVENT_LOG_MSG   1
#define EVENT_LOG_WARN  2
#define EVENT_LOG_ERR   3

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

void event_set_log_callback(event_log_cb cb);

上面的宏定义,是一个severity的等级,下面的是写日记的回调函数。下面是将这个回调函数设置到eventbase里面的。让我们来看看这些函数都是怎么写的。

0719 /**
0720   A callback function used to intercept Libevent's log messages.
0721 
0722   @see event_set_log_callback
0723  */
0724 typedef void (*event_log_cb)(int severity, const char *msg);

这是它原本的定义,只是一个原型的定义。它在/include/event2/event.h和/log.c中,都会被引用到:

在头文件event.h的引用,也是一个函数原型:

0725 /**
0726   Redirect Libevent's log messages.
0727 
0728   @param cb a function taking two arguments: an integer severity between
0729      EVENT_LOG_DEBUG and EVENT_LOG_ERR, and a string.  If cb is NULL,
0730      then the default log is used.
0731 
0732   NOTE: The function you provide *must not* call any other libevent
0733   functionality.  Doing so can produce undefined behavior.
0734   */
0735 EVENT2_EXPORT_SYMBOL
0736 void event_set_log_callback(event_log_cb cb);

在log.c当中,有两处的引用:

0219 static event_log_cb log_fn = NULL;

0221 void
0222 event_set_log_callback(event_log_cb cb)
0223 {
0224     log_fn = cb;
0225 }

这里的代码也很明显,就是设置了一个函数,我继续深挖,它这个函数long_fn,是如何决定它作为记录的函数的?

挖下还是会有的,就在同一个文件当中,有一个 event_log 的函数,用于决定运用哪个函数:我们设置的log_fn 还是 fprintf 

0227 static void
0228 event_log(int severity, const char *msg)
0229 {
0230     if (log_fn)
0231         log_fn(severity, msg);
0232     else {
0233         const char *severity_str;
0234         switch (severity) {
0235         case EVENT_LOG_DEBUG:
0236             severity_str = "debug";
0237             break;
0238         case EVENT_LOG_MSG:
0239             severity_str = "msg";
0240             break;
0241         case EVENT_LOG_WARN:
0242             severity_str = "warn";
0243             break;
0244         case EVENT_LOG_ERR:
0245             severity_str = "err";
0246             break;
0247         default:
0248             severity_str = "???";
0249             break;
0250         }
0251         (void)fprintf(stderr, "[%s] %s
", severity_str, msg);
0252     }
0253 }

要注意的是,在一个用户提供的 eent_log_cb 的回调函数里面,调用libevent的函数,是不安全的。比如说,你要在回调函数里面将错误信息写socket,就不要使用libevent的buffervent这些功能,会产生怪异的,并且难以调试的错误。

This restriction may be removed for some functions in a future version of Libevent.


默认情况下,debug logs 是不会启用的。需要通过以下接口,才能够打开他们:

Interface
#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 或者是所有的它i傲视信息 EVENT_DBG_ALL

event_enable_debug_logging 函数的定义是在/log.c里面:

0085 event_enable_debug_logging(ev_uint32_t which)
0086 {
0087 #ifdef EVENT_DEBUG_LOGGING_ENABLED
0088     event_debug_logging_mask_ = which;
0089 #endif
0090 }

0044 #if !defined(EVENT__DISABLE_DEBUG_MODE) || defined(USE_DEBUG)
0045 #define EVENT_DEBUG_LOGGING_ENABLED
0046 #endif

0073 ev_uint32_t event_debug_logging_mask_ = DEFAULT_MASK;

0065 #ifdef EVENT_DEBUG_LOGGING_ENABLED
0066 #ifdef USE_DEBUG
0067 #define DEFAULT_MASK EVENT_DBG_ALL
0068 #else
0069 #define DEFAULT_MASK 0
0070 #endif

在这里,event_enable_debug_logging 的函数的行为,是根据 EVENT_DEBUG_LOGGING_ENABLED 来进行抉择的。

event_debug_logging_mask_ 也是根据 EVENT_DEBUG_LOGGING_ENABLED 来进行决定DEFAULT_MASK 是0 还是所有。

EVENT_DEBUG_LOGGING_ENABLED 的定义, 是根据 EVENT__DISABLE_DEBUG_MODE 宏和 USE_DEBUG宏来决定的。

这两个宏,在配置文件中是没有的,那么,只要我们在编译的时候,不设置EVENT__DISABLE_DEBUG_MODE 或者是自己设置了USER_DEBUG,就可以有这些功能,也就是,默认是会有这些功能的。


如何处理致命的错误:

可以使用自己的错误处理函数来处理致命的错误:

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

首先,你需要定义一个新的函数,这个函数是Libevent出现致命错误的时候,进行调用的,函数的原型就是 event_fatal_cb。然后再使用event_set_fatal_callback来进行设置。

首先来看一下:event_fatal_cb 的定义和引用的情况:

// /include/event2/event.h

0738 /**
0739    A function to be called if Libevent encounters a fatal internal error.
0740 
0741    @see event_set_fatal_callback
0742  */
0743 typedef void (*event_fatal_cb)(int err);

0745 /**
0746  Override Libevent's behavior in the event of a fatal internal error.
0747 
0748  By default, Libevent will call exit(1) if a programming error makes it
0749  impossible to continue correct operation.  This function allows you to supply
0750  another callback instead.  Note that if the function is ever invoked,
0751  something is wrong with your program, or with Libevent: any subsequent calls
0752  to Libevent may result in undefined behavior.
0753 
0754  Libevent will (almost) always log an EVENT_LOG_ERR message before calling
0755  this function; look at the last log message to see why Libevent has died.
0756  */
0757 EVENT2_EXPORT_SYMBOL
0758 void event_set_fatal_callback(event_fatal_cb cb);

// /log.c
0063 static event_fatal_cb fatal_fn = NULL;

0092 void
0093 event_set_fatal_callback(event_fatal_cb cb)
0094 {
0095     fatal_fn = cb;
0096 }

但是, 在这里, 默认行为就是不处理。

0063 static event_fatal_cb fatal_fn = NULL;

Memory management

默认情况下,Libevent使用C语言库的内存分配函数来进行分配。可以用下面的接口来进行自定义:

// //mm-internal.h

0035 #ifndef EVENT__DISABLE_MM_REPLACEMENT
0036 /* Internal use only: Memory allocation functions. We give them nice short
0037  * mm_names for our own use, but make sure that the symbols have longer names
0038  * so they don't conflict with other libraries (like, say, libmm). */
0039 
0040 /** Allocate uninitialized memory.
0041  *
0042  * @return On success, return a pointer to sz newly allocated bytes.
0043  *     On failure, set errno to ENOMEM and return NULL.
0044  *     If the argument sz is 0, simply return NULL.
0045  */
0046 void *event_mm_malloc_(size_t sz);
0047 
0048 /** Allocate memory initialized to zero.
0049  *
0050  * @return On success, return a pointer to (count * size) newly allocated
0051  *     bytes, initialized to zero.
0052  *     On failure, or if the product would result in an integer overflow,
0053  *     set errno to ENOMEM and return NULL.
0054  *     If either arguments are 0, simply return NULL.
0055  */
0056 void *event_mm_calloc_(size_t count, size_t size);
0057 
0058 /** Duplicate a string.
0059  *
0060  * @return On success, return a pointer to a newly allocated duplicate
0061  *     of a string.
0062  *     Set errno to ENOMEM and return NULL if a memory allocation error
0063  *     occurs (or would occur) in the process.
0064  *     If the argument str is NULL, set errno to EINVAL and return NULL.
0065  */
0066 char *event_mm_strdup_(const char *str);
0067 
0068 void *event_mm_realloc_(void *p, size_t sz);
0069 void event_mm_free_(void *p);
0070 #define mm_malloc(sz) event_mm_malloc_(sz)
0071 #define mm_calloc(count, size) event_mm_calloc_((count), (size))
0072 #define mm_strdup(s) event_mm_strdup_(s)
0073 #define mm_realloc(p, sz) event_mm_realloc_((p), (sz))
0074 #define mm_free(p) event_mm_free_(p)
0075 #else
0076 #define mm_malloc(sz) malloc(sz)
0077 #define mm_calloc(n, sz) calloc((n), (sz))
0078 #define mm_strdup(s) strdup(s)
0079 #define mm_realloc(p, sz) realloc((p), (sz))
0080 #define mm_free(p) free(p)
0081 #endif

取决于 EVENT__DISABLE_MM_REPLACEMENT 的定义,这个定义可以在 /WIN32-Code/nmake/event2/event-config.h下面

/* Define if libevent should not allow replacing the mm functions */
/* #undef EVENT__DISABLE_MM_REPLACEMENT */

定义了这个,就会在建造的时候,无法代替这些内存分配函数。

如果没有定义,那么就会定义下面这几个函数的接口: 

void *event_mm_malloc_(size_t sz);
void *event_mm_calloc_(size_t count, size_t size);
char *event_mm_strdup_(const char *str);
void *event_mm_realloc_(void *p, size_t sz);
void event_mm_free_(void *p);

这些函数的定义,也都在/event.c下面

malloc

3432 void *
3433 event_mm_malloc_(size_t sz)
3434 {
3435     if (sz == 0)
3436         return NULL;
3437 
3438     if (mm_malloc_fn_)
3439         return mm_malloc_fn_(sz);
3440     else
3441         return malloc(sz);
3442 }

calloc函数

3444 void *
3445 event_mm_calloc_(size_t count, size_t size)
3446 {
3447     if (count == 0 || size == 0)
3448         return NULL;
3449 
3450     if (mm_malloc_fn_) {
3451         size_t sz = count * size;
3452         void *p = NULL;
3453         if (count > EV_SIZE_MAX / size)
3454             goto error;
3455         p = mm_malloc_fn_(sz);
3456         if (p)
3457             return memset(p, 0, sz);
3458     } else {
3459         void *p = calloc(count, size);
3460 #ifdef _WIN32
3461         /* Windows calloc doesn't reliably set ENOMEM */
3462         if (p == NULL)
3463             goto error;
3464 #endif
3465         return p;
3466     }
3467 
3468 error:
3469     errno = ENOMEM;
3470     return NULL;
3471 }

strdup的函数,作用是复制一个字符串

3473 char *
3474 event_mm_strdup_(const char *str)
3475 {
3476     if (!str) {
3477         errno = EINVAL;
3478         return NULL;
3479     }
3480 
3481     if (mm_malloc_fn_) {
3482         size_t ln = strlen(str);
3483         void *p = NULL;
3484         if (ln == EV_SIZE_MAX)
3485             goto error;
3486         p = mm_malloc_fn_(ln+1);
3487         if (p)
3488             return memcpy(p, str, ln+1);
3489     } else
3490 #ifdef _WIN32
3491         return _strdup(str);
3492 #else
3493         return strdup(str);
3494 #endif
3495 
3496 error:
3497     errno = ENOMEM;
3498     return NULL;
3499 }

这两个函数realloc和free函数

3501 void *
3502 event_mm_realloc_(void *ptr, size_t sz)
3503 {
3504     if (mm_realloc_fn_)
3505         return mm_realloc_fn_(ptr, sz);
3506     else
3507         return realloc(ptr, sz);
3508 }
3509 

3510 void
3511 event_mm_free_(void *ptr)
3512 {
3513     if (mm_free_fn_)
3514         mm_free_fn_(ptr);
3515     else
3516         free(ptr);
3517 }

不定义的话, 就可以替换 ,使用下面的接口来进行替换

Interface
void event_set_mem_functions(void *(*malloc_fn)(size_t sz),
                             void *(*realloc_fn)(void *ptr, size_t sz),
                             void (*free_fn)(void *ptr));

在上面的实现的基础上,如何实现这个函数,就会比较简单了。只要设置代面里面的 mm_*_fn 就好了

3519 void
3520 event_set_mem_functions(void *(*malloc_fn)(size_t sz),
3521             void *(*realloc_fn)(void *ptr, size_t sz),
3522             void (*free_fn)(void *ptr))
3523 {
3524     mm_malloc_fn_ = malloc_fn;
3525     mm_realloc_fn_ = realloc_fn;
3526     mm_free_fn_ = free_fn;
3527 }

要注意的是,内存分享函数,是会影响所有需要allocate,resize,或free的函数功能的。因此,必须要在任何这些Libevent函数之前。来进行设置。否则,就会一半是用默认的,一半用自己的。

自定义的分配函数,必须是要返回内存的alignment得和C的标准库里面的一样。

你的分配函数必须要正确处理realloc(NULL, sz) 正确,处理方式就是malloc(sz)。

必须要处理realloc(ptr, 0) 的方式为 free(ptr)

free函数不需要处理free(NULL)

不需要处理malloc(0)

内存分配函数必须是threadsafe的

如果替换了malloc,也请使用替换的free来进行释放,因为Libevent会使用这些函数来进行分配和回收。


Locks and threading

多线程的程序,多个线程不可能总是安全的去访问同一个数据。

Libevent 结构一般可以用三种方式来在多个线程中进行工作:

1、一些结构体固有的单个进程的:也就是说,它们都是不能够被多个进程来进行同时访问的

2、一些结构体是可选的locked的:你可以告诉Libevent,哪些object是需要多个进程访问。

3、一些结构体总是被锁上的,因为Libevent总是访问使用锁来进行访问它的。

为了能够在Libevent进行上所,你必须告诉Libevent使用哪个Lock函数。这必须在使用任何函数之前先设置好这个。

如果你使用的是pthreads库,那么你就是幸运的。因为在那个库里面,有与定义的函数,可以用来设置Libevent来使用right pthreads。

接口如下:

        Interface
#ifdef _EVENT_HAVE_PTHREADS
int evthread_use_pthreads(void);
#define EVTHREAD_USE_PTHREADS_IMPLEMENTED
#endif

这些函数的定义和说明如下:

// /include/event2/thread.h

0209 /** Sets up Libevent for use with Pthreads locking and thread ID functions.
0210     Unavailable if Libevent is not build for use with pthreads.  Requires
0211     libraries to link against Libevent_pthreads as well as Libevent.
0212 
0213     @return 0 on success, -1 on failure. */
0214 EVENT2_EXPORT_SYMBOL
0215 int evthread_use_pthreads(void);
// /evthread_pthread.c

0163 int
0164 evthread_use_pthreads(void)
0165 {
0166     struct evthread_lock_callbacks cbs = {
0167         EVTHREAD_LOCK_API_VERSION, 
0168         EVTHREAD_LOCKTYPE_RECURSIVE,
0169         evthread_posix_lock_alloc,
0170         evthread_posix_lock_free,
0171         evthread_posix_lock,
0172         evthread_posix_unlock
0173     };
0174     struct evthread_condition_callbacks cond_cbs = {
0175         EVTHREAD_CONDITION_API_VERSION,
0176         evthread_posix_cond_alloc,
0177         evthread_posix_cond_free,
0178         evthread_posix_cond_signal,
0179         evthread_posix_cond_wait
0180     };
0181     /* Set ourselves up to get recursive locks. */
0182     if (pthread_mutexattr_init(&attr_recursive))
0183         return -1;
0184     if (pthread_mutexattr_settype(&attr_recursive, PTHREAD_MUTEX_RECURSIVE))
0185         return -1;
0186 
0187     evthread_set_lock_callbacks(&cbs);
0188     evthread_set_condition_callbacks(&cond_cbs);
0189     evthread_set_id_callback(evthread_posix_get_id);
0190     return 0;
0191 }

在这个 evthread_use_pthreads 函数当中它做了如下的事情:

struct evthread_lock_callbacks 这个结构体,是用来描述一个多线程库的接口的,这会被用于进行上锁:

0091 /** This structure describes the interface a threading library uses for
0092  * locking.   It's used to tell evthread_set_lock_callbacks() how to use
0093  * locking on this platform.
0094  */
0095 struct evthread_lock_callbacks {
0096     /** The current version of the locking API.  Set this to
0097      * EVTHREAD_LOCK_API_VERSION */
0098     int lock_api_version;
0099     /** Which kinds of locks does this version of the locking API
0100      * support?  A bitfield of EVTHREAD_LOCKTYPE_RECURSIVE and
0101      * EVTHREAD_LOCKTYPE_READWRITE.
0102      *
0103      * (Note that RECURSIVE locks are currently mandatory, and
0104      * READWRITE locks are not currently used.)
0105      **/
0106     unsigned supported_locktypes;
0107     /** Function to allocate and initialize new lock of type 'locktype'.
0108      * Returns NULL on failure. */
0109     void *(*alloc)(unsigned locktype);
0110     /** Funtion to release all storage held in 'lock', which was created
0111      * with type 'locktype'. */
0112     void (*free)(void *lock, unsigned locktype);
0113     /** Acquire an already-allocated lock at 'lock' with mode 'mode'.
0114      * Returns 0 on success, and nonzero on failure. */
0115     int (*lock)(unsigned mode, void *lock);
0116     /** Release a lock at 'lock' using mode 'mode'.  Returns 0 on success,
0117      * and nonzero on failure. */
0118     int (*unlock)(unsigned mode, void *lock);
0119 };

另外一个结构体是struct evthread_condition_callbacks, 这个结构体是用来描写一个线程库的接口用来作条件变量,这是用来告诉 evthread_set_condition_callbacks 要怎么使用locking 在这个平台上:

0136 /** This structure describes the interface a threading library uses for
0137  * condition variables.  It's used to tell evthread_set_condition_callbacks
0138  * how to use locking on this platform.
0139  */
0140 struct evthread_condition_callbacks {
0141     /** The current version of the conditions API.  Set this to
0142      * EVTHREAD_CONDITION_API_VERSION */
0143     int condition_api_version;
0144     /** Function to allocate and initialize a new condition variable.
0145      * Returns the condition variable on success, and NULL on failure.
0146      * The 'condtype' argument will be 0 with this API version.
0147      */
0148     void *(*alloc_condition)(unsigned condtype);
0149     /** Function to free a condition variable. */
0150     void (*free_condition)(void *cond);
0151     /** Function to signal a condition variable.  If 'broadcast' is 1, all
0152      * threads waiting on 'cond' should be woken; otherwise, only on one
0153      * thread is worken.  Should return 0 on success, -1 on failure.
0154      * This function will only be called while holding the associated
0155      * lock for the condition.
0156      */
0157     int (*signal_condition)(void *cond, int broadcast);
0158     /** Function to wait for a condition variable.  The lock 'lock'
0159      * will be held when this function is called; should be released
0160      * while waiting for the condition to be come signalled, and
0161      * should be held again when this function returns.
0162      * If timeout is provided, it is interval of seconds to wait for
0163      * the event to become signalled; if it is NULL, the function
0164      * should wait indefinitely.
0165      *
0166      * The function should return -1 on error; 0 if the condition
0167      * was signalled, or 1 on a timeout. */
0168     int (*wait_condition)(void *cond, void *lock,
0169         const struct timeval *timeout);
0170 };

那么使用之前的函数 evthread_use_pthreads 就很容易理解了,设置两个结构体cbs和cond_cbs,然后再设置mutex为recursive。最后使用 evthread_set_lock_callbacksevthread_set_condition_callbacks ,evthread_set_id_callback 进行设置。这些函数,是在下面进行说明:


如果你要自己实现一个锁,那么你的锁,就需要实现下面的功能:

  • 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_callback 和 evthread_set_id_callback 这些接口

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

这些接口和我们上面的分析几乎是一样的了。那么我们就不再说明 struct evthread_condition_callbacks 和 struct evthread_lock_callbacks了。看一些没有看过的函数的参数和函数。

id_fn 参数,必须是一个函数,会返回一个没有符号的long的标识,能够标识是哪一个线程调用这个函数。

lock_api_version 域必须设置为 EVTHREAD_CONDITION_API_VERSION.

下面来看下两个函数的实现:evthread_set_lock_callbacks 和 evthread_set_condition_callbacks, evthread_get_lock_callbacks

0077 struct evthread_lock_callbacks *evthread_get_lock_callbacks()
0078 {
0079     return evthread_lock_debugging_enabled_
0080         ? &original_lock_fns_ : &evthread_lock_fns_;
0081 }

0054 GLOBAL int evthread_lock_debugging_enabled_ = 0;

0055 GLOBAL struct evthread_lock_callbacks evthread_lock_fns_ = {
0056     0, 0, NULL, NULL, NULL, NULL
0057 };

0063 /* Used for debugging */
0064 static struct evthread_lock_callbacks original_lock_fns_ = {
0065     0, 0, NULL, NULL, NULL, NULL
0066 };

为什么debug 要用另外一个结构体的名字?这我不知道了。

0092 int
0093 evthread_set_lock_callbacks(const struct evthread_lock_callbacks *cbs)
0094 {
0095     struct evthread_lock_callbacks *target = evthread_get_lock_callbacks();
0096 
0097 #ifndef EVENT__DISABLE_DEBUG_MODE
0098     if (event_debug_mode_on_) {
0099         if (event_debug_created_threadable_ctx_) {
0100             event_errx(1, "evthread initialization must be called BEFORE anything else!");
0101         }
0102     }
0103 #endif
0104 
0105     if (!cbs) {
0106         if (target->alloc)
0107             event_warnx("Trying to disable lock functions after "
0108                 "they have been set up will probaby not work.");
0109         memset(target, 0, sizeof(evthread_lock_fns_));
0110         return 0;
0111     }
0112     if (target->alloc) {
0113         /* Uh oh; we already had locking callbacks set up.*/
0114         if (target->lock_api_version == cbs->lock_api_version &&
0115             target->supported_locktypes == cbs->supported_locktypes &&
0116             target->alloc == cbs->alloc &&
0117             target->free == cbs->free &&
0118             target->lock == cbs->lock &&
0119             target->unlock == cbs->unlock) {
0120             /* no change -- allow this. */
0121             return 0;
0122         }
0123         event_warnx("Can't change lock callbacks once they have been "
0124             "initialized.");
0125         return -1;
0126     }
0127     if (cbs->alloc && cbs->free && cbs->lock && cbs->unlock) {
0128         memcpy(target, cbs, sizeof(evthread_lock_fns_));
0129         return event_global_setup_locks_(1);
0130     } else {
0131         return -1;
0132     }
0133 }

这个函数,就是有以下几个逻辑组成: 

  根据是否定义了 EVENT__DISABLE_DEBUG_MODE 这个宏,而产生是否会判断, 调试模式开启,但是却没有初始化这种错误。

  判断cbs是否为空,为空会直接清除调target的内容。 如果已经设置了再进行清除,可能是不起作用的。

  判断cbs和原来的已经设置的是否一致,一致则返回,不一致就会报错,设置了不能进行改变。

  判断cbs是否每个都不为空,如果都不为空,则可以i设置,设置使用的是memcpy函数,最后返回值是 event_global_setup_locks_ 函数。这个函数的过程,会涉及到以下的函数:

event_global_setup_locks_ : 宏 EVTHREAD_SETUP_GLOBAL_LOCK, 函数 evsig_global_setup_locks_,函数 evutil_global_setup_locks_ , evutil_global_setup_locks_,  evutil_secure_rng_global_setup_locks_

宏 EVTHREAD_SETUP_GLOBAL_LOCK 的主要工作是:调用 evthread_setup_global_lock_来给lockvar进行分配一个锁的变量。

evthread_setup_global_lock_:根据四种情况,来给lockvar进行分配锁的变量(根据是否打开调试,是否打开locking)。enable_locks 这个变量,确定是否debug,非0表示debug;original_lock_fns_.alloc 表示locking是否打开,如果等于NULL就是不打开。

后面的几个函数,都是差不多,用来为自己的模块分配一个锁的变量。

最后,没错误,就会返回0给 evthread_set_lock_callbacks 函数。

然后看 evthread_set_condition_callbacks 函数:

0135 int
0136 evthread_set_condition_callbacks(const struct evthread_condition_callbacks *cbs)
0137 {
0138     struct evthread_condition_callbacks *target = evthread_get_condition_callbacks();
0139 
0140 #ifndef EVENT__DISABLE_DEBUG_MODE
0141     if (event_debug_mode_on_) {
0142         if (event_debug_created_threadable_ctx_) {
0143             event_errx(1, "evthread initialization must be called BEFORE anything else!");
0144         }
0145     }
0146 #endif
0147 
0148     if (!cbs) {
0149         if (target->alloc_condition)
0150             event_warnx("Trying to disable condition functions "
0151                 "after they have been set up will probaby not "
0152                 "work.");
0153         memset(target, 0, sizeof(evthread_cond_fns_));
0154         return 0;
0155     }
0156     if (target->alloc_condition) {
0157         /* Uh oh; we already had condition callbacks set up.*/
0158         if (target->condition_api_version == cbs->condition_api_version &&
0159             target->alloc_condition == cbs->alloc_condition &&
0160             target->free_condition == cbs->free_condition &&
0161             target->signal_condition == cbs->signal_condition &&
0162             target->wait_condition == cbs->wait_condition) {
0163             /* no change -- allow this. */
0164             return 0;
0165         }
0166         event_warnx("Can't change condition callbacks once they "
0167             "have been initialized.");
0168         return -1;
0169     }
0170     if (cbs->alloc_condition && cbs->free_condition &&
0171         cbs->signal_condition && cbs->wait_condition) {
0172         memcpy(target, cbs, sizeof(evthread_cond_fns_));
0173     }
0174     if (evthread_lock_debugging_enabled_) {
0175         evthread_cond_fns_.alloc_condition = cbs->alloc_condition;
0176         evthread_cond_fns_.free_condition = cbs->free_condition;
0177         evthread_cond_fns_.signal_condition = cbs->signal_condition;
0178     }
0179     return 0;
0180 }

前面的逻辑和上面的基本是一样的。只由再最后面部分, 根据 evthread_lock_debugging_enabled_ 这个变量,判定是否要将cbs的几个东西,分配给 evthread_cond_fns_ 。 这个变量,在 evthread_enable_lock_debugging()函数中,才会被设为1. 所以,需要调试多线程的锁,应该是一定要调用那个函数。


 

 

原文地址:https://www.cnblogs.com/hwy89289709/p/6970730.html