某人曾提醒我要多讀源代碼,我就選了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. 因此,須要調試多線程的鎖,應該是必定要調用那個函數。