一起讀讀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\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 結構一般可以用三種方式來在多個線程中進行工作:

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. 所以,需要調試多線程的鎖,應該是一定要調用那個函數。


 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM