某人曾提醒我要多读源代码,我就选了libevent 2.1.8稳定版的源代码来读。git
读了一会,纯看源代码里面的东西,还挺无聊的。因此我就开始,便看他们的编程教程:github
http://www.wangafu.net/~nickm/libevent-book/编程
而后每遇到实现,我就跑去源代码中看别人怎么作到的。api
这样仍是比较有趣的,一个一个小目标的去作,直到这个事情是为何而作。安全
我以前,已经把编程的指导粗略看过一边,也是边犯困边看,再开始看源代码,昨天睡了十次八次,才看了很小的一部分。多线程
这样太慢了,并且很无聊。因此换了这样的方式,省去细枝末节,按功能来进行逐个翻看。socket
下面是我我的作这个事的笔记 ide
ps:这里面前面代码里面有数字,是代码的数字,能够参考我以前的设置LXR来查看这些源代码,会比较方便。函数
如何设置log messag:ui
能够经过如下的接口:
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\n", 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;
默认状况下,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会使用这些函数来进行分配和回收。
多线程的程序,多个线程不可能老是安全的去访问同一个数据。
Libevent 结构通常能够用三种方式来在多个线程中进行工做:
一、一些结构体固有的单个进程的:也就是说,它们都是不可以被多个进程来进行同时访问的
二、一些结构体是可选的locked的:你能够告诉Libevent,哪些object是须要多个进程访问。
三、一些结构体老是被锁上的,由于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_callbacks,evthread_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. 因此,须要调试多线程的锁,应该是必定要调用那个函数。