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 
36 struct 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 
44 static pthread_mutexattr_t attr_recursive;
45 
46 static void *
evthread_posix_lock_alloc(unsigned locktype)47 evthread_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 
62 static void
evthread_posix_lock_free(void * lock_,unsigned locktype)63 evthread_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 
70 static int
evthread_posix_lock(unsigned mode,void * lock_)71 evthread_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 
80 static int
evthread_posix_unlock(unsigned mode,void * lock_)81 evthread_posix_unlock(unsigned mode, void *lock_)
82 {
83           pthread_mutex_t *lock = lock_;
84           return pthread_mutex_unlock(lock);
85 }
86 
87 static unsigned long
evthread_posix_get_id(void)88 evthread_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 
105 static void *
evthread_posix_cond_alloc(unsigned condflags)106 evthread_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 
118 static void
evthread_posix_cond_free(void * cond_)119 evthread_posix_cond_free(void *cond_)
120 {
121           pthread_cond_t *cond = cond_;
122           pthread_cond_destroy(cond);
123           mm_free(cond);
124 }
125 
126 static int
evthread_posix_cond_signal(void * cond_,int broadcast)127 evthread_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 
138 static int
evthread_posix_cond_wait(void * cond_,void * lock_,const struct timeval * tv)139 evthread_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 
165 int
evthread_use_pthreads(void)166 evthread_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