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