在 bufferevent-internal.h 中
【bufferevent的锁操作函数】
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
/** Internal: Given a bufferevent, return its corresponding bufferevent_private. */
// 内部使用宏
// 通过 bufferevent 结构获取其所属的 bufferevent_private 结构
#define BEV_UPCAST(b) EVUTIL_UPCAST((b), struct bufferevent_private, bev)
#ifdef _EVENT_DISABLE_THREAD_SUPPORT
#define BEV_LOCK(b) _EVUTIL_NIL_STMT
#define BEV_UNLOCK(b) _EVUTIL_NIL_STMT
#else
/** Internal: Grab the lock (if any) on a bufferevent */
// 内部使用宏
// 对 bufferevent 上锁
#define BEV_LOCK(b) do { \
struct
bufferevent_private *locking = BEV_UPCAST(b); \
EVLOCK_LOCK(locking->lock, 0); \
}
while
(0)
/** Internal: Release the lock (if any) on a bufferevent */
// 内部使用宏
// 对 bufferevent 解锁
#define BEV_UNLOCK(b) do { \
struct
bufferevent_private *locking = BEV_UPCAST(b); \
EVLOCK_UNLOCK(locking->lock, 0); \
}
while
(0)
#endif
|
在 bufferevent.c 中
【bufferevent的锁使能】
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
// 为 bufferevent_private 创建 bufferevent 并初始化内部各结构
int
bufferevent_init_common(
struct
bufferevent_private *bufev_private,
struct
event_base *base,
const
struct
bufferevent_ops *ops,
enum
bufferevent_options options)
{
...
#ifndef _EVENT_DISABLE_THREAD_SUPPORT // 如果编译时使能了线程支持
if
(options & BEV_OPT_THREADSAFE) {
// 并且 bufferevent 也使能了线程支持
if
(bufferevent_enable_locking(bufev, NULL) < 0) {
/* cleanup */
evbuffer_free(bufev->input);
evbuffer_free(bufev->output);
bufev->input = NULL;
bufev->output = NULL;
return
-1;
}
}
#endif
...
}
|
【bufferevent的锁设置】
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
// 为与 bufferevent 相关的各结构设置 lock
// NOTE: 所有结构都共用 lock
int
bufferevent_enable_locking(
struct
bufferevent *bufev,
void
*lock)
{
#ifdef _EVENT_DISABLE_THREAD_SUPPORT
return
-1;
#else
struct
bufferevent *underlying;
// 首先要确保 bufev 所属的 bufferevent_private 中没有 lock
if
(BEV_UPCAST(bufev)->lock)
return
-1;
// 若为 socket 或 pair 类型的 bufferevent 则返回 NULL
underlying = bufferevent_get_underlying(bufev);
// 若外部未提供 lock ,但 bufev 含有底层 bufferevent
// 并且该底层 bufferevent 所属 bufferevent_private 中存在 lock 变量
// 则直接使用该 lock 做为 bufferevent 的锁
if
(!lock && underlying && BEV_UPCAST(underlying)->lock) {
lock = BEV_UPCAST(underlying)->lock;
BEV_UPCAST(bufev)->lock = lock;
BEV_UPCAST(bufev)->own_lock = 0;
}
else
if
(!lock) {
// 外部未提供 lock 且底层也无可用锁
// 则自行分配 lock 做为 bufferevent 的锁
EVTHREAD_ALLOC_LOCK(lock, EVTHREAD_LOCKTYPE_RECURSIVE);
if
(!lock)
return
-1;
BEV_UPCAST(bufev)->lock = lock;
BEV_UPCAST(bufev)->own_lock = 1;
}
else
{
// 外部提供 lock 供直接使用
BEV_UPCAST(bufev)->lock = lock;
BEV_UPCAST(bufev)->own_lock = 0;
}
// 为 input 和 output 缓冲区设置锁
evbuffer_enable_locking(bufev->input, lock);
evbuffer_enable_locking(bufev->output, lock);
if
(underlying && !BEV_UPCAST(underlying)->lock)
bufferevent_enable_locking(underlying, lock);
return
0;
#endif
}
|
在 buffer.c 中
【evbuffer的锁设置】
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
// 为 evbuffer 设置 lock
int
evbuffer_enable_locking(
struct
evbuffer *buf,
void
*lock)
{
#ifdef _EVENT_DISABLE_THREAD_SUPPORT
return
-1;
#else
// 若 evbuffer 内部已存在 lock 则直接返回
if
(buf->lock)
return
-1;
if
(!lock) {
EVTHREAD_ALLOC_LOCK(lock, EVTHREAD_LOCKTYPE_RECURSIVE);
if
(!lock)
return
-1;
buf->lock = lock;
buf->own_lock = 1;
}
else
{
buf->lock = lock;
buf->own_lock = 0;
}
return
0;
#endif
}
|
在 evbuffer-internal.h 中
【evbuffer中的锁定义】
1
2
3
4
5
6
7
8
|
struct
evbuffer {
...
#ifndef _EVENT_DISABLE_THREAD_SUPPORT
/** A lock used to mediate access to this buffer. */
void
*lock;
#endif
...
}
|
在 event-internal.h 中
【event_base的锁定义】
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
struct
event_base {
...
#ifndef _EVENT_DISABLE_THREAD_SUPPORT
/* threading support */
/** The thread currently running the event_loop for this base */
unsigned
long
th_owner_id;
/** A lock to prevent conflicting accesses to this event_base */
// 用于防止针对当前 event_base 冲突性访问的锁
void
*th_base_lock;
/** The event whose callback is executing right now */
struct
event *current_event;
/** A condition that gets signalled when we're done processing an
* event with waiters on it. */
void
*current_event_cond;
/** Number of threads blocking on current_event_cond. */
int
current_event_waiters;
#endif
...
};
|
在 event.c 中
【event_base的锁使能】
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
struct
event_base *
event_base_new_with_config(
const
struct
event_config *cfg)
{
...
#ifndef _EVENT_DISABLE_THREAD_SUPPORT // 如果支持多线程锁
if
(EVTHREAD_LOCKING_ENABLED() &&
// 测试是否锁函数为 NULL ,即 libevent 是否初始化为支持多线程
(!cfg || !(cfg->flags & EVENT_BASE_FLAG_NOLOCK))) {
// 判定当前配置是否支持锁
int
r;
// 申请递归锁
EVTHREAD_ALLOC_LOCK(base->th_base_lock, EVTHREAD_LOCKTYPE_RECURSIVE);
base->defer_queue.lock = base->th_base_lock;
// 申请条件变量
EVTHREAD_ALLOC_COND(base->current_event_cond);
r = evthread_make_base_notifiable(base);
if
(r<0) {
event_warnx(
"%s: Unable to make base notifiable."
, __func__);
event_base_free(base);
return
NULL;
}
}
#endif
...
}
|
在 evthread-internal.h 中
【平台和多线程相关锁定义】
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#ifndef WIN32 // 非 Windows 平台
/* On Windows, the way we currently make DLLs, it's not allowed for us to
* have shared global structures. Thus, we only do the direct-call-to-function
* code path if we know that the local shared library system supports it.
*/
#define EVTHREAD_EXPOSE_STRUCTS
#endif
#if ! defined(_EVENT_DISABLE_THREAD_SUPPORT) && defined(EVTHREAD_EXPOSE_STRUCTS) // 多线程支持+非WIN平台
...
#elif ! defined(_EVENT_DISABLE_THREAD_SUPPORT) // 多线程支持+WIN平台
...
#else /* _EVENT_DISABLE_THREAD_SUPPORT */ // 不支持多线程
...
#endif
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
#if ! defined(_EVENT_DISABLE_THREAD_SUPPORT) && defined(EVTHREAD_EXPOSE_STRUCTS) // 多线程支持+非WIN平台
...
/** Acquire a lock. */
// 获取锁
#define EVLOCK_LOCK(lockvar,mode) \
do
{ \
if
(lockvar) \
_evthread_lock_fns.lock(mode, lockvar); \
}
while
(0)
/** Release a lock */
#define EVLOCK_UNLOCK(lockvar,mode) \
do
{ \
if
(lockvar) \
_evthread_lock_fns.unlock(mode, lockvar); \
}
while
(0)
...
/** Lock an event_base, if it is set up for locking. Acquires the lock
in the base structure whose field is named 'lockvar'. */
// 锁定 event_base ,如果该 event_base 确实支持锁
// 获取 base 结构中的锁,锁名由 lockvar 指定
#define EVBASE_ACQUIRE_LOCK(base, lockvar) do { \
EVLOCK_LOCK((base)->lockvar, 0); \
}
while
(0)
/** Unlock an event_base, if it is set up for locking. */
#define EVBASE_RELEASE_LOCK(base, lockvar) do { \
EVLOCK_UNLOCK((base)->lockvar, 0); \
}
while
(0)
...
#elif ! defined(_EVENT_DISABLE_THREAD_SUPPORT) // 多线程支持+WIN平台
...
|
在 thread.h 中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
|
#if !defined(_EVENT_DISABLE_THREAD_SUPPORT) || defined(_EVENT_IN_DOXYGEN)
#define EVTHREAD_LOCK_API_VERSION 1
/**
@name Types of locks
锁类型
其实这里包含第三种锁类型,即 0 值代表 普通锁
@{*/
/** A recursive lock is one that can be acquired multiple times at once by the
* same thread. No other process can allocate the lock until the thread that
* has been holding it has unlocked it as many times as it locked it. */
// 递归锁类型是指,该锁可以在同一个线程中被获取多次;其他线程无法分配该锁,直到
// 持有该锁的线程对其解锁相同数量的次数
#define EVTHREAD_LOCKTYPE_RECURSIVE 1
/* A read-write lock is one that allows multiple simultaneous readers, but
* where any one writer excludes all other writers and readers. */
#define EVTHREAD_LOCKTYPE_READWRITE 2
/**@}*/
/** This structure describes the interface a threading library uses for
* locking. It's used to tell evthread_set_lock_callbacks() how to use
* locking on this platform.
*/
// 线程锁操作函数指针结构体
// 该结构用于告知 evthread_set_lock_callbacks() 在当前平台上如何使用锁
struct
evthread_lock_callbacks {
/** The current version of the locking API. Set this to
* EVTHREAD_LOCK_API_VERSION */
int
lock_api_version;
/** Which kinds of locks does this version of the locking API
* support? A bitfield of EVTHREAD_LOCKTYPE_RECURSIVE and
* EVTHREAD_LOCKTYPE_READWRITE.
*
* (Note that RECURSIVE locks are currently mandatory, and
* READWRITE locks are not currently used.)
**/
unsigned supported_locktypes;
/** Function to allocate and initialize new lock of type 'locktype'.
* Returns NULL on failure. */
void
*(*alloc)(unsigned locktype);
/** Function to release all storage held in 'lock', which was created
* with type 'locktype'. */
void
(*
free
)(
void
*lock, unsigned locktype);
/** Acquire an already-allocated lock at 'lock' with mode 'mode'.
* Returns 0 on success, and nonzero on failure. */
// 以 'mode' 模式持有由 'lock' 指向的已经分配的锁;0 为获取成功,非零为失败
int
(*lock)(unsigned mode,
void
*lock);
/** Release a lock at 'lock' using mode 'mode'. Returns 0 on success,
* and nonzero on failure. */
int
(*unlock)(unsigned mode,
void
*lock);
};
/** Sets a group of functions that Libevent should use for locking.
* For full information on the required callback API, see the
* documentation for the individual members of evthread_lock_callbacks.
*
* Note that if you're using Windows or the Pthreads threading library, you
* probably shouldn't call this function; instead, use
* evthread_use_windows_threads() or evthread_use_posix_threads() if you can.
*/
int
evthread_set_lock_callbacks(
const
struct
evthread_lock_callbacks *);
...
#if (defined(WIN32) && !defined(_EVENT_DISABLE_THREAD_SUPPORT)) || defined(_EVENT_IN_DOXYGEN)
/** Sets up Libevent for use with Windows builtin locking and thread ID
functions. Unavailable if Libevent is not built for Windows.
@return 0 on success, -1 on failure. */
int
evthread_use_windows_threads(
void
);
// windows 上的锁使能
/**
Defined if Libevent was built with support for evthread_use_windows_threads()
*/
#define EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED 1
#endif
#if defined(_EVENT_HAVE_PTHREADS) || defined(_EVENT_IN_DOXYGEN)
/** Sets up Libevent for use with Pthreads locking and thread ID functions.
Unavailable if Libevent is not build for use with pthreads. Requires
libraries to link against Libevent_pthreads as well as Libevent.
令 libevent 可以使用 pthread 锁和相应的获取线程 id 的函数
如果构建 libevent 时候不支持 pthread 则无法使用该函数
使用时要求链接 libevent_pthreads 库和 libevent 库
@return 0 on success, -1 on failure. */
int
evthread_use_pthreads(
void
);
// linux 上的锁使能
/** Defined if Libevent was built with support for evthread_use_pthreads() */
#define EVTHREAD_USE_PTHREADS_IMPLEMENTED 1
#endif
/** Enable debugging wrappers around the current lock callbacks. If Libevent
* makes one of several common locking errors, exit with an assertion failure.
*
* If you're going to call this function, you must do so before any locks are
* allocated.
**/
void
evthread_enable_lock_debuging(
void
);
#endif /* _EVENT_DISABLE_THREAD_SUPPORT */
|
在 whatsnew-2.0.txt 中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
2.8. evthread_* functions
for
thread-safe structures.
Libevent structures can now be built with locking support. This code
makes it safe to add, remove, and activate events on an event base from a
different thread. (Previously,
if
you wanted to write multithreaded code
with Libevent, you could only an event_base or its events
in
one thread at
a
time
.)
If you want threading support and you're using pthreads, you can just
call evthread_use_pthreads(). (You'll need to link against the
libevent_pthreads library
in
addition to libevent_core. These functions are
not
in
libevent_core.)
If you want threading support and you're using Windows, you can just
call evthread_use_windows_threads().
If you are using some locking system besides Windows and pthreads, You
can
enable
this on a per-event-base level by writing functions to
implement mutexes, conditions, and thread IDs, and passing them to
evthread_set_lock_callbacks and related functions
in
event2
/thread
.h.
Once locking functions are enabled, every new event_base is created with a
lock. You can prevent a single event_base from being built with a lock
disabled by using the EVENT_BASE_FLAG_NOLOCK flag
in
its
event_config. If an event_base is created with a lock, it is safe to call
event_del, event_add, and event_active on its events from any thread. The
event callbacks themselves are still all executed from the thread running
the event loop.
To
make
an evbuffer or a bufferevent object threadsafe, call its
*_enable_locking()
function
.
The HTTP api is not currently threadsafe.
To build Libevent with threading support disabled, pass
--disable-thread-support to the configure script.
|