1/*	$NetBSD: evthread_pthread.c,v 1.5 2020/05/25 20:47:33 christos Exp $	*/
2
3/*
4 * Copyright 2009-2012 Niels Provos and Nick Mathewson
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 *    derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28#include "event2/event-config.h"
29#include "evconfig-private.h"
30
31/* With glibc we need to define _GNU_SOURCE to get PTHREAD_MUTEX_RECURSIVE.
32 * This comes from evconfig-private.h
33 */
34#include <pthread.h>
35
36struct event_base;
37#include "event2/thread.h"
38
39#include <stdlib.h>
40#include <string.h>
41#include "mm-internal.h"
42#include "evthread-internal.h"
43
44static pthread_mutexattr_t attr_recursive;
45
46static void *
47evthread_posix_lock_alloc(unsigned locktype)
48{
49	pthread_mutexattr_t *attr = NULL;
50	pthread_mutex_t *lock = mm_malloc(sizeof(pthread_mutex_t));
51	if (!lock)
52		return NULL;
53	if (locktype & EVTHREAD_LOCKTYPE_RECURSIVE)
54		attr = &attr_recursive;
55	if (pthread_mutex_init(lock, attr)) {
56		mm_free(lock);
57		return NULL;
58	}
59	return lock;
60}
61
62static void
63evthread_posix_lock_free(void *lock_, unsigned locktype)
64{
65	pthread_mutex_t *lock = lock_;
66	pthread_mutex_destroy(lock);
67	mm_free(lock);
68}
69
70static int
71evthread_posix_lock(unsigned mode, void *lock_)
72{
73	pthread_mutex_t *lock = lock_;
74	if (mode & EVTHREAD_TRY)
75		return pthread_mutex_trylock(lock);
76	else
77		return pthread_mutex_lock(lock);
78}
79
80static int
81evthread_posix_unlock(unsigned mode, void *lock_)
82{
83	pthread_mutex_t *lock = lock_;
84	return pthread_mutex_unlock(lock);
85}
86
87static unsigned long
88evthread_posix_get_id(void)
89{
90	union {
91		pthread_t thr;
92#if EVENT__SIZEOF_PTHREAD_T > EVENT__SIZEOF_LONG
93		ev_uint64_t id;
94#else
95		unsigned long id;
96#endif
97	} r;
98#if EVENT__SIZEOF_PTHREAD_T < EVENT__SIZEOF_LONG
99	memset(&r, 0, sizeof(r));
100#endif
101	r.thr = pthread_self();
102	return (unsigned long)r.id;
103}
104
105static void *
106evthread_posix_cond_alloc(unsigned condflags)
107{
108	pthread_cond_t *cond = mm_malloc(sizeof(pthread_cond_t));
109	if (!cond)
110		return NULL;
111	if (pthread_cond_init(cond, NULL)) {
112		mm_free(cond);
113		return NULL;
114	}
115	return cond;
116}
117
118static void
119evthread_posix_cond_free(void *cond_)
120{
121	pthread_cond_t *cond = cond_;
122	pthread_cond_destroy(cond);
123	mm_free(cond);
124}
125
126static int
127evthread_posix_cond_signal(void *cond_, int broadcast)
128{
129	pthread_cond_t *cond = cond_;
130	int r;
131	if (broadcast)
132		r = pthread_cond_broadcast(cond);
133	else
134		r = pthread_cond_signal(cond);
135	return r ? -1 : 0;
136}
137
138static int
139evthread_posix_cond_wait(void *cond_, void *lock_, const struct timeval *tv)
140{
141	int r;
142	pthread_cond_t *cond = cond_;
143	pthread_mutex_t *lock = lock_;
144
145	if (tv) {
146		struct timeval now, abstime;
147		struct timespec ts;
148		evutil_gettimeofday(&now, NULL);
149		evutil_timeradd(&now, tv, &abstime);
150		ts.tv_sec = abstime.tv_sec;
151		ts.tv_nsec = abstime.tv_usec*1000;
152		r = pthread_cond_timedwait(cond, lock, &ts);
153		if (r == ETIMEDOUT)
154			return 1;
155		else if (r)
156			return -1;
157		else
158			return 0;
159	} else {
160		r = pthread_cond_wait(cond, lock);
161		return r ? -1 : 0;
162	}
163}
164
165int
166evthread_use_pthreads(void)
167{
168	struct evthread_lock_callbacks cbs = {
169		EVTHREAD_LOCK_API_VERSION,
170		EVTHREAD_LOCKTYPE_RECURSIVE,
171		evthread_posix_lock_alloc,
172		evthread_posix_lock_free,
173		evthread_posix_lock,
174		evthread_posix_unlock
175	};
176	struct evthread_condition_callbacks cond_cbs = {
177		EVTHREAD_CONDITION_API_VERSION,
178		evthread_posix_cond_alloc,
179		evthread_posix_cond_free,
180		evthread_posix_cond_signal,
181		evthread_posix_cond_wait
182	};
183	/* Set ourselves up to get recursive locks. */
184	if (pthread_mutexattr_init(&attr_recursive))
185		return -1;
186	if (pthread_mutexattr_settype(&attr_recursive, PTHREAD_MUTEX_RECURSIVE))
187		return -1;
188
189	evthread_set_lock_callbacks(&cbs);
190	evthread_set_condition_callbacks(&cond_cbs);
191	evthread_set_id_callback(evthread_posix_get_id);
192	return 0;
193}
194