一塊兒讀讀libevent的源代碼:Libevent 第一章 設置libevent (1)

某人曾提醒我要多讀源代碼,我就選了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;

 


 

 

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 結構通常能夠用三種方式來在多個線程中進行工做:

一、一些結構體固有的單個進程的:也就是說,它們都是不可以被多個進程來進行同時訪問的

二、一些結構體是可選的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_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. 因此,須要調試多線程的鎖,應該是必定要調用那個函數。

相關文章
相關標籤/搜索