调试 lock 的用法:
使用这个方法,我们能够捕获以下两种的lock的错误:
-
unlocking a lock that we don’t actually hold
-
re-locking a non-recursive lock
在之前的分析,我们知道它的其中一部分是通过 evthread_lock_debugging_enabled_ 这变量来进行的。但具体怎么样,来一起深挖一下
Interface
void evthread_enable_lock_debugging(void);
#define evthread_enable_lock_debuging() evthread_enable_lock_debugging()
// /evthread.c
0317 void
0318 evthread_enable_lock_debugging(void)
0319 {
0320 struct evthread_lock_callbacks cbs = {
0321 EVTHREAD_LOCK_API_VERSION,
0322 EVTHREAD_LOCKTYPE_RECURSIVE,
0323 debug_lock_alloc,
0324 debug_lock_free,
0325 debug_lock_lock,
0326 debug_lock_unlock
0327 };
0328 if (evthread_lock_debugging_enabled_)
0329 return;
0330 memcpy(&original_lock_fns_, &evthread_lock_fns_,
0331 sizeof(struct evthread_lock_callbacks));
0332 memcpy(&evthread_lock_fns_, &cbs,
0333 sizeof(struct evthread_lock_callbacks));
0334
0335 memcpy(&original_cond_fns_, &evthread_cond_fns_,
0336 sizeof(struct evthread_condition_callbacks));
0337 evthread_cond_fns_.wait_condition = debug_cond_wait;
0338 evthread_lock_debugging_enabled_ = 1;
0339
0340 /* XXX return value should get checked. */
0341 event_global_setup_locks_(0);
0342 }
这里首先是要设置 cbs 这个 struct evthread_lock_callbacks 结构, 然后就分开三步,将evthread_lock_fns_ 复制到 original_lock_fns_ , 将 cbs 复制到evthread_lock_fns_ ,然后再将 evthread_cond_fns_ 复制到 original_cond_fns_ 。
我想,这里为什么要这么复制,是因为函数 evthread_get_lock_callbacks() 和 evthread_get_condition_callbacks() 函数,都会需要判断 evthread_lock_debugging_enabled_,然后取决于使用 original_* 和 evthread_* 这两个结构。evthread_lock_debugging_enabled_ 为真的时候,就会使用 original_* 的这个变量。至于,为什么这么做,我还不知道。
另外一个知识:
struct evthread_lock_callbacks 是用于记录ethread_lock_callbacks 来记录锁的分配函数等等。
在文件/event.c 里面,用于保存锁的变量是:static void *event_debug_map_lock_,分配给它的锁,是ethread里面的evthread_setup_global_lock_来进行分配的
在文件/signal.c 里面,用于保存锁的变量是:static void *evsig_base_lock, 分配锁给它的evthread_setup_global_lock_。
同理,/evutil.c 里面,保存锁的变量就是 :static void *windows_socket_errors_lock_, 分配锁的也是同一个函数。
但/evutil_rand.c 里面,暂时没有锁的变量,虽然上面的代码也写了分配。
从这上面的理解,就可以知道了,其实ethread的是一个分配锁的机构,因为会有不同的线程访问event, signal,evutil等。所以需要这些锁来进行同步。
Debugging event usage
有一些在使用events的常见的错误,Libevent可以检测并且向你报告。这些错误包括:
将一个未初始化的event结构,当成是已经初始化的
尝试再初始化一个挂起的event结构体
接口如下:
Interface
void event_enable_debug_mode(void);
This function must only be called before any event_base is created.
函数的代码如下:
0529 void
0530 event_enable_debug_mode(void)
0531 {
0532 #ifndef EVENT__DISABLE_DEBUG_MODE
0533 if (event_debug_mode_on_)
0534 event_errx(1, "%s was called twice!", __func__);
0535 if (event_debug_mode_too_late)
0536 event_errx(1, "%s must be called *before* creating any events "
0537 "or event_bases",__func__);
0538
0539 event_debug_mode_on_ = 1;
0540
0541 HT_INIT(event_debug_map, &global_debug_map);
0542 #endif
0543 }
这里的作用,就是设置一个变量为1,并且初始化了一个hash_table。并且初始化了一个 event_debug_map。那么这么一个变量event_debug_mode_on_ 会被用到哪里呢?我们可以看到一下的函数:
/* Macro: record that ev is now setup (that is, ready for an add) */
event_debug_note_setup_(ev)
/* Macro: record that ev is no longer setup */
event_debug_note_teardown_(ev)
/* Macro: record that ev is now added */
event_debug_note_add_(ev)
/* Macro: record that ev is no longer added */
event_debug_note_del_(ev)
/* Macro: assert that ev is setup (i.e., okay to add or inspect) */
event_debug_assert_is_setup_(ev)
/* Macro: assert that ev is not added (i.e., okay to tear down or set
* up again) */
event_debug_assert_not_added_(ev)
函数 : event_disable_debug_mode(void)
在说明中,有这么一句话,如果使用debug mode,你可能会run out of memory。因为是使用了event_assign(),而不是event_new()。这是因为Libevent没有办法区分event 是使用event_assign() 创建,并且不再使用。但是event_new()是可以区分的,因为你使用event_free()来进行释放它。同样的,必须要使用以igehanshu,来告诉Libevent这样的一个事件,不再被看看作是assigned的。
Interface
void event_debug_unassign(struct event *ev);
函数的源代码是:
2186 void
2187 event_debug_unassign(struct event *ev)
2188 {
2189 event_debug_assert_not_added_(ev);
2190 event_debug_note_teardown_(ev);
2191
2192 ev->ev_flags &= ~EVLIST_INIT;
2193 }
这里面,挺多每搞懂的。先放着。看其他的先。
Detailed event debugging 只能用这样一种方法来进行开启,编译的时候添加如此的标志:-DUSE_DEBUG
会包含下面的信息,event的添加,event的删除,平台指定的通知信息。
Detecting the version of Libevent:
可以用下面的接口来检测Libevent的版本信息。
Interface #define LIBEVENT_VERSION_NUMBER 0x02000300 #define LIBEVENT_VERSION "2.0.3-alpha" const char *event_get_version(void); ev_uint32_t event_get_version_number(void);
这里面有不同的:宏是再编译的时候,确定了Libevent的库;函数返回的是运行时的版本。注意,如果你有动态链接你的程序到Libevent,这些版本可能是不同的。
3404 const char * 3405 event_get_version(void) 3406 { 3407 return (EVENT__VERSION); 3408 } 0333 /* Version number of package */ 0334 #define EVENT__VERSION "2.1.8-stable"
另外一个的源代码是:
3411 event_get_version_number(void) 3412 { 3413 return (EVENT__NUMERIC_VERSION); 3414 } 0276 /* Numeric representation of the version */ 0277 #define EVENT__NUMERIC_VERSION 0x02010800
Freeing global Libevent structures:
接口:
Interface void libevent_global_shutdown(void);
3851 void 3852 libevent_global_shutdown(void) 3853 { 3854 event_disable_debug_mode(); 3855 event_free_globals(); 3856 }
首先 event_disable_debug_mode() 会释放调锁由Hash_table上面的锁由的事件入口:
0545 void 0546 event_disable_debug_mode(void) 0547 { 0548 #ifndef EVENT__DISABLE_DEBUG_MODE 0549 struct event_debug_entry **ent, *victim; 0550 0551 EVLOCK_LOCK(event_debug_map_lock_, 0); 0552 for (ent = HT_START(event_debug_map, &global_debug_map); ent; ) { 0553 victim = *ent; 0554 ent = HT_NEXT_RMV(event_debug_map, &global_debug_map, ent); 0555 mm_free(victim); 0556 } 0557 HT_CLEAR(event_debug_map, &global_debug_map); 0558 EVLOCK_UNLOCK(event_debug_map_lock_ , 0); 0559 0560 event_debug_mode_on_ = 0; 0561 #endif 0562 }
然后使用event_free_globals()函数进行释放
3843 static void 3844 event_free_globals(void) 3845 { 3846 event_free_debug_globals(); 3847 event_free_evsig_globals(); 3848 event_free_evutil_globals(); 3849 }
这三个函数,最主要的都是释放三个模块当中锁包含的全局的锁的结构。