evthread.c revision 290001
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
72struct evthread_lock_callbacks *evthread_get_lock_callbacks()
73{
74	return evthread_lock_debugging_enabled_
75	    ? &original_lock_fns_ : &evthread_lock_fns_;
76}
77struct evthread_condition_callbacks *evthread_get_condition_callbacks()
78{
79	return evthread_lock_debugging_enabled_
80	    ? &original_cond_fns_ : &evthread_cond_fns_;
81}
82void evthreadimpl_disable_lock_debugging_(void)
83{
84	evthread_lock_debugging_enabled_ = 0;
85}
86
87int
88evthread_set_lock_callbacks(const struct evthread_lock_callbacks *cbs)
89{
90	struct evthread_lock_callbacks *target = evthread_get_lock_callbacks();
91
92	if (!cbs) {
93		if (target->alloc)
94			event_warnx("Trying to disable lock functions after "
95			    "they have been set up will probaby not work.");
96		memset(target, 0, sizeof(evthread_lock_fns_));
97		return 0;
98	}
99	if (target->alloc) {
100		/* Uh oh; we already had locking callbacks set up.*/
101		if (target->lock_api_version == cbs->lock_api_version &&
102			target->supported_locktypes == cbs->supported_locktypes &&
103			target->alloc == cbs->alloc &&
104			target->free == cbs->free &&
105			target->lock == cbs->lock &&
106			target->unlock == cbs->unlock) {
107			/* no change -- allow this. */
108			return 0;
109		}
110		event_warnx("Can't change lock callbacks once they have been "
111		    "initialized.");
112		return -1;
113	}
114	if (cbs->alloc && cbs->free && cbs->lock && cbs->unlock) {
115		memcpy(target, cbs, sizeof(evthread_lock_fns_));
116		return event_global_setup_locks_(1);
117	} else {
118		return -1;
119	}
120}
121
122int
123evthread_set_condition_callbacks(const struct evthread_condition_callbacks *cbs)
124{
125	struct evthread_condition_callbacks *target = evthread_get_condition_callbacks();
126
127	if (!cbs) {
128		if (target->alloc_condition)
129			event_warnx("Trying to disable condition functions "
130			    "after they have been set up will probaby not "
131			    "work.");
132		memset(target, 0, sizeof(evthread_cond_fns_));
133		return 0;
134	}
135	if (target->alloc_condition) {
136		/* Uh oh; we already had condition callbacks set up.*/
137		if (target->condition_api_version == cbs->condition_api_version &&
138			target->alloc_condition == cbs->alloc_condition &&
139			target->free_condition == cbs->free_condition &&
140			target->signal_condition == cbs->signal_condition &&
141			target->wait_condition == cbs->wait_condition) {
142			/* no change -- allow this. */
143			return 0;
144		}
145		event_warnx("Can't change condition callbacks once they "
146		    "have been initialized.");
147		return -1;
148	}
149	if (cbs->alloc_condition && cbs->free_condition &&
150	    cbs->signal_condition && cbs->wait_condition) {
151		memcpy(target, cbs, sizeof(evthread_cond_fns_));
152	}
153	if (evthread_lock_debugging_enabled_) {
154		evthread_cond_fns_.alloc_condition = cbs->alloc_condition;
155		evthread_cond_fns_.free_condition = cbs->free_condition;
156		evthread_cond_fns_.signal_condition = cbs->signal_condition;
157	}
158	return 0;
159}
160
161#define DEBUG_LOCK_SIG	0xdeb0b10c
162
163struct debug_lock {
164	unsigned signature;
165	unsigned locktype;
166	unsigned long held_by;
167	/* XXXX if we ever use read-write locks, we will need a separate
168	 * lock to protect count. */
169	int count;
170	void *lock;
171};
172
173static void *
174debug_lock_alloc(unsigned locktype)
175{
176	struct debug_lock *result = mm_malloc(sizeof(struct debug_lock));
177	if (!result)
178		return NULL;
179	if (original_lock_fns_.alloc) {
180		if (!(result->lock = original_lock_fns_.alloc(
181				locktype|EVTHREAD_LOCKTYPE_RECURSIVE))) {
182			mm_free(result);
183			return NULL;
184		}
185	} else {
186		result->lock = NULL;
187	}
188	result->signature = DEBUG_LOCK_SIG;
189	result->locktype = locktype;
190	result->count = 0;
191	result->held_by = 0;
192	return result;
193}
194
195static void
196debug_lock_free(void *lock_, unsigned locktype)
197{
198	struct debug_lock *lock = lock_;
199	EVUTIL_ASSERT(lock->count == 0);
200	EVUTIL_ASSERT(locktype == lock->locktype);
201	EVUTIL_ASSERT(DEBUG_LOCK_SIG == lock->signature);
202	if (original_lock_fns_.free) {
203		original_lock_fns_.free(lock->lock,
204		    lock->locktype|EVTHREAD_LOCKTYPE_RECURSIVE);
205	}
206	lock->lock = NULL;
207	lock->count = -100;
208	lock->signature = 0x12300fda;
209	mm_free(lock);
210}
211
212static void
213evthread_debug_lock_mark_locked(unsigned mode, struct debug_lock *lock)
214{
215	EVUTIL_ASSERT(DEBUG_LOCK_SIG == lock->signature);
216	++lock->count;
217	if (!(lock->locktype & EVTHREAD_LOCKTYPE_RECURSIVE))
218		EVUTIL_ASSERT(lock->count == 1);
219	if (evthread_id_fn_) {
220		unsigned long me;
221		me = evthread_id_fn_();
222		if (lock->count > 1)
223			EVUTIL_ASSERT(lock->held_by == me);
224		lock->held_by = me;
225	}
226}
227
228static int
229debug_lock_lock(unsigned mode, void *lock_)
230{
231	struct debug_lock *lock = lock_;
232	int res = 0;
233	if (lock->locktype & EVTHREAD_LOCKTYPE_READWRITE)
234		EVUTIL_ASSERT(mode & (EVTHREAD_READ|EVTHREAD_WRITE));
235	else
236		EVUTIL_ASSERT((mode & (EVTHREAD_READ|EVTHREAD_WRITE)) == 0);
237	if (original_lock_fns_.lock)
238		res = original_lock_fns_.lock(mode, lock->lock);
239	if (!res) {
240		evthread_debug_lock_mark_locked(mode, lock);
241	}
242	return res;
243}
244
245static void
246evthread_debug_lock_mark_unlocked(unsigned mode, struct debug_lock *lock)
247{
248	EVUTIL_ASSERT(DEBUG_LOCK_SIG == lock->signature);
249	if (lock->locktype & EVTHREAD_LOCKTYPE_READWRITE)
250		EVUTIL_ASSERT(mode & (EVTHREAD_READ|EVTHREAD_WRITE));
251	else
252		EVUTIL_ASSERT((mode & (EVTHREAD_READ|EVTHREAD_WRITE)) == 0);
253	if (evthread_id_fn_) {
254		unsigned long me;
255		me = evthread_id_fn_();
256		EVUTIL_ASSERT(lock->held_by == me);
257		if (lock->count == 1)
258			lock->held_by = 0;
259	}
260	--lock->count;
261	EVUTIL_ASSERT(lock->count >= 0);
262}
263
264static int
265debug_lock_unlock(unsigned mode, void *lock_)
266{
267	struct debug_lock *lock = lock_;
268	int res = 0;
269	evthread_debug_lock_mark_unlocked(mode, lock);
270	if (original_lock_fns_.unlock)
271		res = original_lock_fns_.unlock(mode, lock->lock);
272	return res;
273}
274
275static int
276debug_cond_wait(void *cond_, void *lock_, const struct timeval *tv)
277{
278	int r;
279	struct debug_lock *lock = lock_;
280	EVUTIL_ASSERT(lock);
281	EVUTIL_ASSERT(DEBUG_LOCK_SIG == lock->signature);
282	EVLOCK_ASSERT_LOCKED(lock_);
283	evthread_debug_lock_mark_unlocked(0, lock);
284	r = original_cond_fns_.wait_condition(cond_, lock->lock, tv);
285	evthread_debug_lock_mark_locked(0, lock);
286	return r;
287}
288
289/* misspelled version for backward compatibility */
290void
291evthread_enable_lock_debuging(void)
292{
293	evthread_enable_lock_debugging();
294}
295
296void
297evthread_enable_lock_debugging(void)
298{
299	struct evthread_lock_callbacks cbs = {
300		EVTHREAD_LOCK_API_VERSION,
301		EVTHREAD_LOCKTYPE_RECURSIVE,
302		debug_lock_alloc,
303		debug_lock_free,
304		debug_lock_lock,
305		debug_lock_unlock
306	};
307	if (evthread_lock_debugging_enabled_)
308		return;
309	memcpy(&original_lock_fns_, &evthread_lock_fns_,
310	    sizeof(struct evthread_lock_callbacks));
311	memcpy(&evthread_lock_fns_, &cbs,
312	    sizeof(struct evthread_lock_callbacks));
313
314	memcpy(&original_cond_fns_, &evthread_cond_fns_,
315	    sizeof(struct evthread_condition_callbacks));
316	evthread_cond_fns_.wait_condition = debug_cond_wait;
317	evthread_lock_debugging_enabled_ = 1;
318
319	/* XXX return value should get checked. */
320	event_global_setup_locks_(0);
321}
322
323int
324evthread_is_debug_lock_held_(void *lock_)
325{
326	struct debug_lock *lock = lock_;
327	if (! lock->count)
328		return 0;
329	if (evthread_id_fn_) {
330		unsigned long me = evthread_id_fn_();
331		if (lock->held_by != me)
332			return 0;
333	}
334	return 1;
335}
336
337void *
338evthread_debug_get_real_lock_(void *lock_)
339{
340	struct debug_lock *lock = lock_;
341	return lock->lock;
342}
343
344void *
345evthread_setup_global_lock_(void *lock_, unsigned locktype, int enable_locks)
346{
347	/* there are four cases here:
348	   1) we're turning on debugging; locking is not on.
349	   2) we're turning on debugging; locking is on.
350	   3) we're turning on locking; debugging is not on.
351	   4) we're turning on locking; debugging is on. */
352
353	if (!enable_locks && original_lock_fns_.alloc == NULL) {
354		/* Case 1: allocate a debug lock. */
355		EVUTIL_ASSERT(lock_ == NULL);
356		return debug_lock_alloc(locktype);
357	} else if (!enable_locks && original_lock_fns_.alloc != NULL) {
358		/* Case 2: wrap the lock in a debug lock. */
359		struct debug_lock *lock;
360		EVUTIL_ASSERT(lock_ != NULL);
361
362		if (!(locktype & EVTHREAD_LOCKTYPE_RECURSIVE)) {
363			/* We can't wrap it: We need a recursive lock */
364			original_lock_fns_.free(lock_, locktype);
365			return debug_lock_alloc(locktype);
366		}
367		lock = mm_malloc(sizeof(struct debug_lock));
368		if (!lock) {
369			original_lock_fns_.free(lock_, locktype);
370			return NULL;
371		}
372		lock->lock = lock_;
373		lock->locktype = locktype;
374		lock->count = 0;
375		lock->held_by = 0;
376		return lock;
377	} else if (enable_locks && ! evthread_lock_debugging_enabled_) {
378		/* Case 3: allocate a regular lock */
379		EVUTIL_ASSERT(lock_ == NULL);
380		return evthread_lock_fns_.alloc(locktype);
381	} else {
382		/* Case 4: Fill in a debug lock with a real lock */
383		struct debug_lock *lock = lock_;
384		EVUTIL_ASSERT(enable_locks &&
385		              evthread_lock_debugging_enabled_);
386		EVUTIL_ASSERT(lock->locktype == locktype);
387		EVUTIL_ASSERT(lock->lock == NULL);
388		lock->lock = original_lock_fns_.alloc(
389			locktype|EVTHREAD_LOCKTYPE_RECURSIVE);
390		if (!lock->lock) {
391			lock->count = -200;
392			mm_free(lock);
393			return NULL;
394		}
395		return lock;
396	}
397}
398
399
400#ifndef EVTHREAD_EXPOSE_STRUCTS
401unsigned long
402evthreadimpl_get_id_()
403{
404	return evthread_id_fn_ ? evthread_id_fn_() : 1;
405}
406void *
407evthreadimpl_lock_alloc_(unsigned locktype)
408{
409	return evthread_lock_fns_.alloc ?
410	    evthread_lock_fns_.alloc(locktype) : NULL;
411}
412void
413evthreadimpl_lock_free_(void *lock, unsigned locktype)
414{
415	if (evthread_lock_fns_.free)
416		evthread_lock_fns_.free(lock, locktype);
417}
418int
419evthreadimpl_lock_lock_(unsigned mode, void *lock)
420{
421	if (evthread_lock_fns_.lock)
422		return evthread_lock_fns_.lock(mode, lock);
423	else
424		return 0;
425}
426int
427evthreadimpl_lock_unlock_(unsigned mode, void *lock)
428{
429	if (evthread_lock_fns_.unlock)
430		return evthread_lock_fns_.unlock(mode, lock);
431	else
432		return 0;
433}
434void *
435evthreadimpl_cond_alloc_(unsigned condtype)
436{
437	return evthread_cond_fns_.alloc_condition ?
438	    evthread_cond_fns_.alloc_condition(condtype) : NULL;
439}
440void
441evthreadimpl_cond_free_(void *cond)
442{
443	if (evthread_cond_fns_.free_condition)
444		evthread_cond_fns_.free_condition(cond);
445}
446int
447evthreadimpl_cond_signal_(void *cond, int broadcast)
448{
449	if (evthread_cond_fns_.signal_condition)
450		return evthread_cond_fns_.signal_condition(cond, broadcast);
451	else
452		return 0;
453}
454int
455evthreadimpl_cond_wait_(void *cond, void *lock, const struct timeval *tv)
456{
457	if (evthread_cond_fns_.wait_condition)
458		return evthread_cond_fns_.wait_condition(cond, lock, tv);
459	else
460		return 0;
461}
462int
463evthreadimpl_is_lock_debugging_enabled_(void)
464{
465	return evthread_lock_debugging_enabled_;
466}
467
468int
469evthreadimpl_locking_enabled_(void)
470{
471	return evthread_lock_fns_.lock != NULL;
472}
473#endif
474
475#endif
476