evthread.c revision 275970
1/*
2 * Copyright (c) 2008-2012 Niels Provos, Nick Mathewson
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 * 3. The name of the author may not be used to endorse or promote products
13 *    derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "event2/event-config.h"
28#include "evconfig-private.h"
29
30#ifndef EVENT__DISABLE_THREAD_SUPPORT
31
32#include "event2/thread.h"
33
34#include <stdlib.h>
35#include <string.h>
36
37#include "log-internal.h"
38#include "mm-internal.h"
39#include "util-internal.h"
40#include "evthread-internal.h"
41
42#ifdef EVTHREAD_EXPOSE_STRUCTS
43#define GLOBAL
44#else
45#define GLOBAL static
46#endif
47
48/* globals */
49GLOBAL int evthread_lock_debugging_enabled_ = 0;
50GLOBAL struct evthread_lock_callbacks evthread_lock_fns_ = {
51	0, 0, NULL, NULL, NULL, NULL
52};
53GLOBAL unsigned long (*evthread_id_fn_)(void) = NULL;
54GLOBAL struct evthread_condition_callbacks evthread_cond_fns_ = {
55	0, NULL, NULL, NULL, NULL
56};
57
58/* Used for debugging */
59static struct evthread_lock_callbacks original_lock_fns_ = {
60	0, 0, NULL, NULL, NULL, NULL
61};
62static struct evthread_condition_callbacks original_cond_fns_ = {
63	0, NULL, NULL, NULL, NULL
64};
65
66void
67evthread_set_id_callback(unsigned long (*id_fn)(void))
68{
69	evthread_id_fn_ = id_fn;
70}
71
72int
73evthread_set_lock_callbacks(const struct evthread_lock_callbacks *cbs)
74{
75	struct evthread_lock_callbacks *target =
76	    evthread_lock_debugging_enabled_
77	    ? &original_lock_fns_ : &evthread_lock_fns_;
78
79	if (!cbs) {
80		if (target->alloc)
81			event_warnx("Trying to disable lock functions after "
82			    "they have been set up will probaby not work.");
83		memset(target, 0, sizeof(evthread_lock_fns_));
84		return 0;
85	}
86	if (target->alloc) {
87		/* Uh oh; we already had locking callbacks set up.*/
88		if (target->lock_api_version == cbs->lock_api_version &&
89			target->supported_locktypes == cbs->supported_locktypes &&
90			target->alloc == cbs->alloc &&
91			target->free == cbs->free &&
92			target->lock == cbs->lock &&
93			target->unlock == cbs->unlock) {
94			/* no change -- allow this. */
95			return 0;
96		}
97		event_warnx("Can't change lock callbacks once they have been "
98		    "initialized.");
99		return -1;
100	}
101	if (cbs->alloc && cbs->free && cbs->lock && cbs->unlock) {
102		memcpy(target, cbs, sizeof(evthread_lock_fns_));
103		return event_global_setup_locks_(1);
104	} else {
105		return -1;
106	}
107}
108
109int
110evthread_set_condition_callbacks(const struct evthread_condition_callbacks *cbs)
111{
112	struct evthread_condition_callbacks *target =
113	    evthread_lock_debugging_enabled_
114	    ? &original_cond_fns_ : &evthread_cond_fns_;
115
116	if (!cbs) {
117		if (target->alloc_condition)
118			event_warnx("Trying to disable condition functions "
119			    "after they have been set up will probaby not "
120			    "work.");
121		memset(target, 0, sizeof(evthread_cond_fns_));
122		return 0;
123	}
124	if (target->alloc_condition) {
125		/* Uh oh; we already had condition callbacks set up.*/
126		if (target->condition_api_version == cbs->condition_api_version &&
127			target->alloc_condition == cbs->alloc_condition &&
128			target->free_condition == cbs->free_condition &&
129			target->signal_condition == cbs->signal_condition &&
130			target->wait_condition == cbs->wait_condition) {
131			/* no change -- allow this. */
132			return 0;
133		}
134		event_warnx("Can't change condition callbacks once they "
135		    "have been initialized.");
136		return -1;
137	}
138	if (cbs->alloc_condition && cbs->free_condition &&
139	    cbs->signal_condition && cbs->wait_condition) {
140		memcpy(target, cbs, sizeof(evthread_cond_fns_));
141	}
142	if (evthread_lock_debugging_enabled_) {
143		evthread_cond_fns_.alloc_condition = cbs->alloc_condition;
144		evthread_cond_fns_.free_condition = cbs->free_condition;
145		evthread_cond_fns_.signal_condition = cbs->signal_condition;
146	}
147	return 0;
148}
149
150#define DEBUG_LOCK_SIG	0xdeb0b10c
151
152struct debug_lock {
153	unsigned signature;
154	unsigned locktype;
155	unsigned long held_by;
156	/* XXXX if we ever use read-write locks, we will need a separate
157	 * lock to protect count. */
158	int count;
159	void *lock;
160};
161
162static void *
163debug_lock_alloc(unsigned locktype)
164{
165	struct debug_lock *result = mm_malloc(sizeof(struct debug_lock));
166	if (!result)
167		return NULL;
168	if (original_lock_fns_.alloc) {
169		if (!(result->lock = original_lock_fns_.alloc(
170				locktype|EVTHREAD_LOCKTYPE_RECURSIVE))) {
171			mm_free(result);
172			return NULL;
173		}
174	} else {
175		result->lock = NULL;
176	}
177	result->signature = DEBUG_LOCK_SIG;
178	result->locktype = locktype;
179	result->count = 0;
180	result->held_by = 0;
181	return result;
182}
183
184static void
185debug_lock_free(void *lock_, unsigned locktype)
186{
187	struct debug_lock *lock = lock_;
188	EVUTIL_ASSERT(lock->count == 0);
189	EVUTIL_ASSERT(locktype == lock->locktype);
190	EVUTIL_ASSERT(DEBUG_LOCK_SIG == lock->signature);
191	if (original_lock_fns_.free) {
192		original_lock_fns_.free(lock->lock,
193		    lock->locktype|EVTHREAD_LOCKTYPE_RECURSIVE);
194	}
195	lock->lock = NULL;
196	lock->count = -100;
197	lock->signature = 0x12300fda;
198	mm_free(lock);
199}
200
201static void
202evthread_debug_lock_mark_locked(unsigned mode, struct debug_lock *lock)
203{
204	EVUTIL_ASSERT(DEBUG_LOCK_SIG == lock->signature);
205	++lock->count;
206	if (!(lock->locktype & EVTHREAD_LOCKTYPE_RECURSIVE))
207		EVUTIL_ASSERT(lock->count == 1);
208	if (evthread_id_fn_) {
209		unsigned long me;
210		me = evthread_id_fn_();
211		if (lock->count > 1)
212			EVUTIL_ASSERT(lock->held_by == me);
213		lock->held_by = me;
214	}
215}
216
217static int
218debug_lock_lock(unsigned mode, void *lock_)
219{
220	struct debug_lock *lock = lock_;
221	int res = 0;
222	if (lock->locktype & EVTHREAD_LOCKTYPE_READWRITE)
223		EVUTIL_ASSERT(mode & (EVTHREAD_READ|EVTHREAD_WRITE));
224	else
225		EVUTIL_ASSERT((mode & (EVTHREAD_READ|EVTHREAD_WRITE)) == 0);
226	if (original_lock_fns_.lock)
227		res = original_lock_fns_.lock(mode, lock->lock);
228	if (!res) {
229		evthread_debug_lock_mark_locked(mode, lock);
230	}
231	return res;
232}
233
234static void
235evthread_debug_lock_mark_unlocked(unsigned mode, struct debug_lock *lock)
236{
237	EVUTIL_ASSERT(DEBUG_LOCK_SIG == lock->signature);
238	if (lock->locktype & EVTHREAD_LOCKTYPE_READWRITE)
239		EVUTIL_ASSERT(mode & (EVTHREAD_READ|EVTHREAD_WRITE));
240	else
241		EVUTIL_ASSERT((mode & (EVTHREAD_READ|EVTHREAD_WRITE)) == 0);
242	if (evthread_id_fn_) {
243		unsigned long me;
244		me = evthread_id_fn_();
245		EVUTIL_ASSERT(lock->held_by == me);
246		if (lock->count == 1)
247			lock->held_by = 0;
248	}
249	--lock->count;
250	EVUTIL_ASSERT(lock->count >= 0);
251}
252
253static int
254debug_lock_unlock(unsigned mode, void *lock_)
255{
256	struct debug_lock *lock = lock_;
257	int res = 0;
258	evthread_debug_lock_mark_unlocked(mode, lock);
259	if (original_lock_fns_.unlock)
260		res = original_lock_fns_.unlock(mode, lock->lock);
261	return res;
262}
263
264static int
265debug_cond_wait(void *cond_, void *lock_, const struct timeval *tv)
266{
267	int r;
268	struct debug_lock *lock = lock_;
269	EVUTIL_ASSERT(lock);
270	EVUTIL_ASSERT(DEBUG_LOCK_SIG == lock->signature);
271	EVLOCK_ASSERT_LOCKED(lock_);
272	evthread_debug_lock_mark_unlocked(0, lock);
273	r = original_cond_fns_.wait_condition(cond_, lock->lock, tv);
274	evthread_debug_lock_mark_locked(0, lock);
275	return r;
276}
277
278/* misspelled version for backward compatibility */
279void
280evthread_enable_lock_debuging(void)
281{
282	evthread_enable_lock_debugging();
283}
284
285void
286evthread_enable_lock_debugging(void)
287{
288	struct evthread_lock_callbacks cbs = {
289		EVTHREAD_LOCK_API_VERSION,
290		EVTHREAD_LOCKTYPE_RECURSIVE,
291		debug_lock_alloc,
292		debug_lock_free,
293		debug_lock_lock,
294		debug_lock_unlock
295	};
296	if (evthread_lock_debugging_enabled_)
297		return;
298	memcpy(&original_lock_fns_, &evthread_lock_fns_,
299	    sizeof(struct evthread_lock_callbacks));
300	memcpy(&evthread_lock_fns_, &cbs,
301	    sizeof(struct evthread_lock_callbacks));
302
303	memcpy(&original_cond_fns_, &evthread_cond_fns_,
304	    sizeof(struct evthread_condition_callbacks));
305	evthread_cond_fns_.wait_condition = debug_cond_wait;
306	evthread_lock_debugging_enabled_ = 1;
307
308	/* XXX return value should get checked. */
309	event_global_setup_locks_(0);
310}
311
312int
313evthread_is_debug_lock_held_(void *lock_)
314{
315	struct debug_lock *lock = lock_;
316	if (! lock->count)
317		return 0;
318	if (evthread_id_fn_) {
319		unsigned long me = evthread_id_fn_();
320		if (lock->held_by != me)
321			return 0;
322	}
323	return 1;
324}
325
326void *
327evthread_debug_get_real_lock_(void *lock_)
328{
329	struct debug_lock *lock = lock_;
330	return lock->lock;
331}
332
333void *
334evthread_setup_global_lock_(void *lock_, unsigned locktype, int enable_locks)
335{
336	/* there are four cases here:
337	   1) we're turning on debugging; locking is not on.
338	   2) we're turning on debugging; locking is on.
339	   3) we're turning on locking; debugging is not on.
340	   4) we're turning on locking; debugging is on. */
341
342	if (!enable_locks && original_lock_fns_.alloc == NULL) {
343		/* Case 1: allocate a debug lock. */
344		EVUTIL_ASSERT(lock_ == NULL);
345		return debug_lock_alloc(locktype);
346	} else if (!enable_locks && original_lock_fns_.alloc != NULL) {
347		/* Case 2: wrap the lock in a debug lock. */
348		struct debug_lock *lock;
349		EVUTIL_ASSERT(lock_ != NULL);
350
351		if (!(locktype & EVTHREAD_LOCKTYPE_RECURSIVE)) {
352			/* We can't wrap it: We need a recursive lock */
353			original_lock_fns_.free(lock_, locktype);
354			return debug_lock_alloc(locktype);
355		}
356		lock = mm_malloc(sizeof(struct debug_lock));
357		if (!lock) {
358			original_lock_fns_.free(lock_, locktype);
359			return NULL;
360		}
361		lock->lock = lock_;
362		lock->locktype = locktype;
363		lock->count = 0;
364		lock->held_by = 0;
365		return lock;
366	} else if (enable_locks && ! evthread_lock_debugging_enabled_) {
367		/* Case 3: allocate a regular lock */
368		EVUTIL_ASSERT(lock_ == NULL);
369		return evthread_lock_fns_.alloc(locktype);
370	} else {
371		/* Case 4: Fill in a debug lock with a real lock */
372		struct debug_lock *lock = lock_;
373		EVUTIL_ASSERT(enable_locks &&
374		              evthread_lock_debugging_enabled_);
375		EVUTIL_ASSERT(lock->locktype == locktype);
376		EVUTIL_ASSERT(lock->lock == NULL);
377		lock->lock = original_lock_fns_.alloc(
378			locktype|EVTHREAD_LOCKTYPE_RECURSIVE);
379		if (!lock->lock) {
380			lock->count = -200;
381			mm_free(lock);
382			return NULL;
383		}
384		return lock;
385	}
386}
387
388
389#ifndef EVTHREAD_EXPOSE_STRUCTS
390unsigned long
391evthreadimpl_get_id_()
392{
393	return evthread_id_fn_ ? evthread_id_fn_() : 1;
394}
395void *
396evthreadimpl_lock_alloc_(unsigned locktype)
397{
398	return evthread_lock_fns_.alloc ?
399	    evthread_lock_fns_.alloc(locktype) : NULL;
400}
401void
402evthreadimpl_lock_free_(void *lock, unsigned locktype)
403{
404	if (evthread_lock_fns_.free)
405		evthread_lock_fns_.free(lock, locktype);
406}
407int
408evthreadimpl_lock_lock_(unsigned mode, void *lock)
409{
410	if (evthread_lock_fns_.lock)
411		return evthread_lock_fns_.lock(mode, lock);
412	else
413		return 0;
414}
415int
416evthreadimpl_lock_unlock_(unsigned mode, void *lock)
417{
418	if (evthread_lock_fns_.unlock)
419		return evthread_lock_fns_.unlock(mode, lock);
420	else
421		return 0;
422}
423void *
424evthreadimpl_cond_alloc_(unsigned condtype)
425{
426	return evthread_cond_fns_.alloc_condition ?
427	    evthread_cond_fns_.alloc_condition(condtype) : NULL;
428}
429void
430evthreadimpl_cond_free_(void *cond)
431{
432	if (evthread_cond_fns_.free_condition)
433		evthread_cond_fns_.free_condition(cond);
434}
435int
436evthreadimpl_cond_signal_(void *cond, int broadcast)
437{
438	if (evthread_cond_fns_.signal_condition)
439		return evthread_cond_fns_.signal_condition(cond, broadcast);
440	else
441		return 0;
442}
443int
444evthreadimpl_cond_wait_(void *cond, void *lock, const struct timeval *tv)
445{
446	if (evthread_cond_fns_.wait_condition)
447		return evthread_cond_fns_.wait_condition(cond, lock, tv);
448	else
449		return 0;
450}
451int
452evthreadimpl_is_lock_debugging_enabled_(void)
453{
454	return evthread_lock_debugging_enabled_;
455}
456
457int
458evthreadimpl_locking_enabled_(void)
459{
460	return evthread_lock_fns_.lock != NULL;
461}
462#endif
463
464#endif
465