1290001Sglebius/*
2290001Sglebius * Copyright 2009-2012 Niels Provos and Nick Mathewson
3290001Sglebius *
4290001Sglebius * Redistribution and use in source and binary forms, with or without
5290001Sglebius * modification, are permitted provided that the following conditions
6290001Sglebius * are met:
7290001Sglebius * 1. Redistributions of source code must retain the above copyright
8290001Sglebius *    notice, this list of conditions and the following disclaimer.
9290001Sglebius * 2. Redistributions in binary form must reproduce the above copyright
10290001Sglebius *    notice, this list of conditions and the following disclaimer in the
11290001Sglebius *    documentation and/or other materials provided with the distribution.
12290001Sglebius * 3. The name of the author may not be used to endorse or promote products
13290001Sglebius *    derived from this software without specific prior written permission.
14290001Sglebius *
15290001Sglebius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16290001Sglebius * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17290001Sglebius * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18290001Sglebius * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19290001Sglebius * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20290001Sglebius * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21290001Sglebius * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22290001Sglebius * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23290001Sglebius * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24290001Sglebius * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25290001Sglebius */
26290001Sglebius#include "event2/event-config.h"
27290001Sglebius#include "evconfig-private.h"
28290001Sglebius
29290001Sglebius/* With glibc we need to define _GNU_SOURCE to get PTHREAD_MUTEX_RECURSIVE.
30290001Sglebius * This comes from evconfig-private.h
31290001Sglebius */
32290001Sglebius#include <pthread.h>
33290001Sglebius
34290001Sglebiusstruct event_base;
35290001Sglebius#include "event2/thread.h"
36290001Sglebius
37290001Sglebius#include <stdlib.h>
38290001Sglebius#include <string.h>
39290001Sglebius#include "mm-internal.h"
40290001Sglebius#include "evthread-internal.h"
41290001Sglebius
42290001Sglebiusstatic pthread_mutexattr_t attr_recursive;
43290001Sglebius
44290001Sglebiusstatic void *
45290001Sglebiusevthread_posix_lock_alloc(unsigned locktype)
46290001Sglebius{
47290001Sglebius	pthread_mutexattr_t *attr = NULL;
48290001Sglebius	pthread_mutex_t *lock = mm_malloc(sizeof(pthread_mutex_t));
49290001Sglebius	if (!lock)
50290001Sglebius		return NULL;
51290001Sglebius	if (locktype & EVTHREAD_LOCKTYPE_RECURSIVE)
52290001Sglebius		attr = &attr_recursive;
53290001Sglebius	if (pthread_mutex_init(lock, attr)) {
54290001Sglebius		mm_free(lock);
55290001Sglebius		return NULL;
56290001Sglebius	}
57290001Sglebius	return lock;
58290001Sglebius}
59290001Sglebius
60290001Sglebiusstatic void
61290001Sglebiusevthread_posix_lock_free(void *lock_, unsigned locktype)
62290001Sglebius{
63290001Sglebius	pthread_mutex_t *lock = lock_;
64290001Sglebius	pthread_mutex_destroy(lock);
65290001Sglebius	mm_free(lock);
66290001Sglebius}
67290001Sglebius
68290001Sglebiusstatic int
69290001Sglebiusevthread_posix_lock(unsigned mode, void *lock_)
70290001Sglebius{
71290001Sglebius	pthread_mutex_t *lock = lock_;
72290001Sglebius	if (mode & EVTHREAD_TRY)
73290001Sglebius		return pthread_mutex_trylock(lock);
74290001Sglebius	else
75290001Sglebius		return pthread_mutex_lock(lock);
76290001Sglebius}
77290001Sglebius
78290001Sglebiusstatic int
79290001Sglebiusevthread_posix_unlock(unsigned mode, void *lock_)
80290001Sglebius{
81290001Sglebius	pthread_mutex_t *lock = lock_;
82290001Sglebius	return pthread_mutex_unlock(lock);
83290001Sglebius}
84290001Sglebius
85290001Sglebiusstatic unsigned long
86290001Sglebiusevthread_posix_get_id(void)
87290001Sglebius{
88290001Sglebius	union {
89290001Sglebius		pthread_t thr;
90290001Sglebius#if EVENT__SIZEOF_PTHREAD_T > EVENT__SIZEOF_LONG
91290001Sglebius		ev_uint64_t id;
92290001Sglebius#else
93290001Sglebius		unsigned long id;
94290001Sglebius#endif
95290001Sglebius	} r;
96290001Sglebius#if EVENT__SIZEOF_PTHREAD_T < EVENT__SIZEOF_LONG
97290001Sglebius	memset(&r, 0, sizeof(r));
98290001Sglebius#endif
99290001Sglebius	r.thr = pthread_self();
100290001Sglebius	return (unsigned long)r.id;
101290001Sglebius}
102290001Sglebius
103290001Sglebiusstatic void *
104290001Sglebiusevthread_posix_cond_alloc(unsigned condflags)
105290001Sglebius{
106290001Sglebius	pthread_cond_t *cond = mm_malloc(sizeof(pthread_cond_t));
107290001Sglebius	if (!cond)
108290001Sglebius		return NULL;
109290001Sglebius	if (pthread_cond_init(cond, NULL)) {
110290001Sglebius		mm_free(cond);
111290001Sglebius		return NULL;
112290001Sglebius	}
113290001Sglebius	return cond;
114290001Sglebius}
115290001Sglebius
116290001Sglebiusstatic void
117290001Sglebiusevthread_posix_cond_free(void *cond_)
118290001Sglebius{
119290001Sglebius	pthread_cond_t *cond = cond_;
120290001Sglebius	pthread_cond_destroy(cond);
121290001Sglebius	mm_free(cond);
122290001Sglebius}
123290001Sglebius
124290001Sglebiusstatic int
125290001Sglebiusevthread_posix_cond_signal(void *cond_, int broadcast)
126290001Sglebius{
127290001Sglebius	pthread_cond_t *cond = cond_;
128290001Sglebius	int r;
129290001Sglebius	if (broadcast)
130290001Sglebius		r = pthread_cond_broadcast(cond);
131290001Sglebius	else
132290001Sglebius		r = pthread_cond_signal(cond);
133290001Sglebius	return r ? -1 : 0;
134290001Sglebius}
135290001Sglebius
136290001Sglebiusstatic int
137290001Sglebiusevthread_posix_cond_wait(void *cond_, void *lock_, const struct timeval *tv)
138290001Sglebius{
139290001Sglebius	int r;
140290001Sglebius	pthread_cond_t *cond = cond_;
141290001Sglebius	pthread_mutex_t *lock = lock_;
142290001Sglebius
143290001Sglebius	if (tv) {
144290001Sglebius		struct timeval now, abstime;
145290001Sglebius		struct timespec ts;
146290001Sglebius		evutil_gettimeofday(&now, NULL);
147290001Sglebius		evutil_timeradd(&now, tv, &abstime);
148290001Sglebius		ts.tv_sec = abstime.tv_sec;
149290001Sglebius		ts.tv_nsec = abstime.tv_usec*1000;
150290001Sglebius		r = pthread_cond_timedwait(cond, lock, &ts);
151290001Sglebius		if (r == ETIMEDOUT)
152290001Sglebius			return 1;
153290001Sglebius		else if (r)
154290001Sglebius			return -1;
155290001Sglebius		else
156290001Sglebius			return 0;
157290001Sglebius	} else {
158290001Sglebius		r = pthread_cond_wait(cond, lock);
159290001Sglebius		return r ? -1 : 0;
160290001Sglebius	}
161290001Sglebius}
162290001Sglebius
163290001Sglebiusint
164290001Sglebiusevthread_use_pthreads(void)
165290001Sglebius{
166290001Sglebius	struct evthread_lock_callbacks cbs = {
167290001Sglebius		EVTHREAD_LOCK_API_VERSION,
168290001Sglebius		EVTHREAD_LOCKTYPE_RECURSIVE,
169290001Sglebius		evthread_posix_lock_alloc,
170290001Sglebius		evthread_posix_lock_free,
171290001Sglebius		evthread_posix_lock,
172290001Sglebius		evthread_posix_unlock
173290001Sglebius	};
174290001Sglebius	struct evthread_condition_callbacks cond_cbs = {
175290001Sglebius		EVTHREAD_CONDITION_API_VERSION,
176290001Sglebius		evthread_posix_cond_alloc,
177290001Sglebius		evthread_posix_cond_free,
178290001Sglebius		evthread_posix_cond_signal,
179290001Sglebius		evthread_posix_cond_wait
180290001Sglebius	};
181290001Sglebius	/* Set ourselves up to get recursive locks. */
182290001Sglebius	if (pthread_mutexattr_init(&attr_recursive))
183290001Sglebius		return -1;
184290001Sglebius	if (pthread_mutexattr_settype(&attr_recursive, PTHREAD_MUTEX_RECURSIVE))
185290001Sglebius		return -1;
186290001Sglebius
187290001Sglebius	evthread_set_lock_callbacks(&cbs);
188290001Sglebius	evthread_set_condition_callbacks(&cond_cbs);
189290001Sglebius	evthread_set_id_callback(evthread_posix_get_id);
190290001Sglebius	return 0;
191290001Sglebius}
192