evthread_pthread.c revision 290001
1/*
2 * Copyright 2009-2012 Niels Provos and 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#include "event2/event-config.h"
27#include "evconfig-private.h"
28
29/* With glibc we need to define _GNU_SOURCE to get PTHREAD_MUTEX_RECURSIVE.
30 * This comes from evconfig-private.h
31 */
32#include <pthread.h>
33
34struct event_base;
35#include "event2/thread.h"
36
37#include <stdlib.h>
38#include <string.h>
39#include "mm-internal.h"
40#include "evthread-internal.h"
41
42static pthread_mutexattr_t attr_recursive;
43
44static void *
45evthread_posix_lock_alloc(unsigned locktype)
46{
47	pthread_mutexattr_t *attr = NULL;
48	pthread_mutex_t *lock = mm_malloc(sizeof(pthread_mutex_t));
49	if (!lock)
50		return NULL;
51	if (locktype & EVTHREAD_LOCKTYPE_RECURSIVE)
52		attr = &attr_recursive;
53	if (pthread_mutex_init(lock, attr)) {
54		mm_free(lock);
55		return NULL;
56	}
57	return lock;
58}
59
60static void
61evthread_posix_lock_free(void *lock_, unsigned locktype)
62{
63	pthread_mutex_t *lock = lock_;
64	pthread_mutex_destroy(lock);
65	mm_free(lock);
66}
67
68static int
69evthread_posix_lock(unsigned mode, void *lock_)
70{
71	pthread_mutex_t *lock = lock_;
72	if (mode & EVTHREAD_TRY)
73		return pthread_mutex_trylock(lock);
74	else
75		return pthread_mutex_lock(lock);
76}
77
78static int
79evthread_posix_unlock(unsigned mode, void *lock_)
80{
81	pthread_mutex_t *lock = lock_;
82	return pthread_mutex_unlock(lock);
83}
84
85static unsigned long
86evthread_posix_get_id(void)
87{
88	union {
89		pthread_t thr;
90#if EVENT__SIZEOF_PTHREAD_T > EVENT__SIZEOF_LONG
91		ev_uint64_t id;
92#else
93		unsigned long id;
94#endif
95	} r;
96#if EVENT__SIZEOF_PTHREAD_T < EVENT__SIZEOF_LONG
97	memset(&r, 0, sizeof(r));
98#endif
99	r.thr = pthread_self();
100	return (unsigned long)r.id;
101}
102
103static void *
104evthread_posix_cond_alloc(unsigned condflags)
105{
106	pthread_cond_t *cond = mm_malloc(sizeof(pthread_cond_t));
107	if (!cond)
108		return NULL;
109	if (pthread_cond_init(cond, NULL)) {
110		mm_free(cond);
111		return NULL;
112	}
113	return cond;
114}
115
116static void
117evthread_posix_cond_free(void *cond_)
118{
119	pthread_cond_t *cond = cond_;
120	pthread_cond_destroy(cond);
121	mm_free(cond);
122}
123
124static int
125evthread_posix_cond_signal(void *cond_, int broadcast)
126{
127	pthread_cond_t *cond = cond_;
128	int r;
129	if (broadcast)
130		r = pthread_cond_broadcast(cond);
131	else
132		r = pthread_cond_signal(cond);
133	return r ? -1 : 0;
134}
135
136static int
137evthread_posix_cond_wait(void *cond_, void *lock_, const struct timeval *tv)
138{
139	int r;
140	pthread_cond_t *cond = cond_;
141	pthread_mutex_t *lock = lock_;
142
143	if (tv) {
144		struct timeval now, abstime;
145		struct timespec ts;
146		evutil_gettimeofday(&now, NULL);
147		evutil_timeradd(&now, tv, &abstime);
148		ts.tv_sec = abstime.tv_sec;
149		ts.tv_nsec = abstime.tv_usec*1000;
150		r = pthread_cond_timedwait(cond, lock, &ts);
151		if (r == ETIMEDOUT)
152			return 1;
153		else if (r)
154			return -1;
155		else
156			return 0;
157	} else {
158		r = pthread_cond_wait(cond, lock);
159		return r ? -1 : 0;
160	}
161}
162
163int
164evthread_use_pthreads(void)
165{
166	struct evthread_lock_callbacks cbs = {
167		EVTHREAD_LOCK_API_VERSION,
168		EVTHREAD_LOCKTYPE_RECURSIVE,
169		evthread_posix_lock_alloc,
170		evthread_posix_lock_free,
171		evthread_posix_lock,
172		evthread_posix_unlock
173	};
174	struct evthread_condition_callbacks cond_cbs = {
175		EVTHREAD_CONDITION_API_VERSION,
176		evthread_posix_cond_alloc,
177		evthread_posix_cond_free,
178		evthread_posix_cond_signal,
179		evthread_posix_cond_wait
180	};
181	/* Set ourselves up to get recursive locks. */
182	if (pthread_mutexattr_init(&attr_recursive))
183		return -1;
184	if (pthread_mutexattr_settype(&attr_recursive, PTHREAD_MUTEX_RECURSIVE))
185		return -1;
186
187	evthread_set_lock_callbacks(&cbs);
188	evthread_set_condition_callbacks(&cond_cbs);
189	evthread_set_id_callback(evthread_posix_get_id);
190	return 0;
191}
192