1 /*
2  * virthread.c: basic thread synchronization primitives
3  *
4  * Copyright (C) 2009-2010, 2014 Red Hat, Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library.  If not, see
18  * <http://www.gnu.org/licenses/>.
19  *
20  */
21 
22 #include <config.h>
23 
24 #include "virthread.h"
25 
26 #ifdef __FreeBSD__
27 # include <pthread_np.h>
28 #endif
29 
30 #include <unistd.h>
31 #include <inttypes.h>
32 #if WITH_SYS_SYSCALL_H
33 # include <sys/syscall.h>
34 #endif
35 
36 #include "viralloc.h"
37 #include "virthreadjob.h"
38 
39 
virOnce(virOnceControl * once,virOnceFunc init)40 int virOnce(virOnceControl *once, virOnceFunc init)
41 {
42     int ret;
43 
44     ret = pthread_once(&once->once, init);
45     if (ret != 0) {
46         errno = ret;
47         return -1;
48     }
49 
50     return 0;
51 }
52 
53 
virMutexInit(virMutex * m)54 int virMutexInit(virMutex *m)
55 {
56     int ret;
57     pthread_mutexattr_t attr;
58     pthread_mutexattr_init(&attr);
59     pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
60     ret = pthread_mutex_init(&m->lock, &attr);
61     pthread_mutexattr_destroy(&attr);
62     if (ret != 0) {
63         errno = ret;
64         return -1;
65     }
66     return 0;
67 }
68 
virMutexInitRecursive(virMutex * m)69 int virMutexInitRecursive(virMutex *m)
70 {
71     int ret;
72     pthread_mutexattr_t attr;
73     pthread_mutexattr_init(&attr);
74     pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
75     ret = pthread_mutex_init(&m->lock, &attr);
76     pthread_mutexattr_destroy(&attr);
77     if (ret != 0) {
78         errno = ret;
79         return -1;
80     }
81     return 0;
82 }
83 
virMutexDestroy(virMutex * m)84 void virMutexDestroy(virMutex *m)
85 {
86     pthread_mutex_destroy(&m->lock);
87 }
88 
virMutexLock(virMutex * m)89 void virMutexLock(virMutex *m)
90 {
91     pthread_mutex_lock(&m->lock);
92 }
93 
virMutexUnlock(virMutex * m)94 void virMutexUnlock(virMutex *m)
95 {
96     pthread_mutex_unlock(&m->lock);
97 }
98 
99 
virRWLockInit(virRWLock * m)100 int virRWLockInit(virRWLock *m)
101 {
102     int ret;
103     ret = pthread_rwlock_init(&m->lock, NULL);
104     if (ret != 0) {
105         errno = ret;
106         return -1;
107     }
108     return 0;
109 }
110 
virRWLockDestroy(virRWLock * m)111 void virRWLockDestroy(virRWLock *m)
112 {
113     pthread_rwlock_destroy(&m->lock);
114 }
115 
116 
virRWLockRead(virRWLock * m)117 void virRWLockRead(virRWLock *m)
118 {
119     pthread_rwlock_rdlock(&m->lock);
120 }
121 
virRWLockWrite(virRWLock * m)122 void virRWLockWrite(virRWLock *m)
123 {
124     pthread_rwlock_wrlock(&m->lock);
125 }
126 
127 
virRWLockUnlock(virRWLock * m)128 void virRWLockUnlock(virRWLock *m)
129 {
130     pthread_rwlock_unlock(&m->lock);
131 }
132 
virCondInit(virCond * c)133 int virCondInit(virCond *c)
134 {
135     int ret;
136     if ((ret = pthread_cond_init(&c->cond, NULL)) != 0) {
137         errno = ret;
138         return -1;
139     }
140     return 0;
141 }
142 
virCondDestroy(virCond * c)143 int virCondDestroy(virCond *c)
144 {
145     int ret;
146     if ((ret = pthread_cond_destroy(&c->cond)) != 0) {
147         errno = ret;
148         return -1;
149     }
150     return 0;
151 }
152 
virCondWait(virCond * c,virMutex * m)153 int virCondWait(virCond *c, virMutex *m)
154 {
155     int ret;
156     if ((ret = pthread_cond_wait(&c->cond, &m->lock)) != 0) {
157         errno = ret;
158         return -1;
159     }
160     return 0;
161 }
162 
virCondWaitUntil(virCond * c,virMutex * m,unsigned long long whenms)163 int virCondWaitUntil(virCond *c, virMutex *m, unsigned long long whenms)
164 {
165     int ret;
166     struct timespec ts;
167 
168     ts.tv_sec = whenms / 1000;
169     ts.tv_nsec = (whenms % 1000) * 1000000;
170 
171     if ((ret = pthread_cond_timedwait(&c->cond, &m->lock, &ts)) != 0) {
172         errno = ret;
173         return -1;
174     }
175     return 0;
176 }
177 
virCondSignal(virCond * c)178 void virCondSignal(virCond *c)
179 {
180     pthread_cond_signal(&c->cond);
181 }
182 
virCondBroadcast(virCond * c)183 void virCondBroadcast(virCond *c)
184 {
185     pthread_cond_broadcast(&c->cond);
186 }
187 
188 struct virThreadArgs {
189     virThreadFunc func;
190     char *name;
191     bool worker;
192     void *opaque;
193 };
194 
virThreadMaxName(void)195 size_t virThreadMaxName(void)
196 {
197 #if defined(__FreeBSD__) || defined(__APPLE__)
198     return 63;
199 #else
200 # ifdef __linux__
201     return 15;
202 # else
203     return 0; /* unlimited */
204 # endif
205 #endif
206 }
207 
virThreadHelper(void * data)208 static void *virThreadHelper(void *data)
209 {
210     struct virThreadArgs *args = data;
211     struct virThreadArgs local = *args;
212     g_autofree char *thname = NULL;
213     size_t maxname = virThreadMaxName();
214 
215     /* Free args early, rather than tying it up during the entire thread.  */
216     g_free(args);
217 
218     if (local.worker)
219         virThreadJobSetWorker(local.name);
220     else
221         virThreadJobSet(local.name);
222 
223     if (maxname) {
224         thname = g_strndup(local.name, maxname);
225     } else {
226         thname = g_strdup(local.name);
227     }
228 
229 #if defined(__linux__) || defined(WIN32)
230     pthread_setname_np(pthread_self(), thname);
231 #else
232 # ifdef __FreeBSD__
233     pthread_set_name_np(pthread_self(), thname);
234 # else
235 #  ifdef __APPLE__
236     pthread_setname_np(thname);
237 #  endif
238 # endif
239 #endif
240 
241     local.func(local.opaque);
242 
243     if (!local.worker)
244         virThreadJobClear(0);
245 
246     g_free(local.name);
247     return NULL;
248 }
249 
virThreadCreateFull(virThread * thread,bool joinable,virThreadFunc func,const char * name,bool worker,void * opaque)250 int virThreadCreateFull(virThread *thread,
251                         bool joinable,
252                         virThreadFunc func,
253                         const char *name,
254                         bool worker,
255                         void *opaque)
256 {
257     struct virThreadArgs *args;
258     pthread_attr_t attr;
259     int ret = -1;
260     int err;
261 
262     if ((err = pthread_attr_init(&attr)) != 0)
263         goto cleanup;
264 
265     args = g_new0(struct virThreadArgs, 1);
266     args->func = func;
267     args->name = g_strdup(name);
268     args->worker = worker;
269     args->opaque = opaque;
270 
271     if (!joinable)
272         pthread_attr_setdetachstate(&attr, 1);
273 
274     err = pthread_create(&thread->thread, &attr, virThreadHelper, args);
275     if (err != 0) {
276         g_free(args->name);
277         g_free(args);
278         goto cleanup;
279     }
280     /* New thread owns 'args' in success case, so don't free */
281 
282     ret = 0;
283  cleanup:
284     pthread_attr_destroy(&attr);
285     if (ret < 0)
286         errno = err;
287     return ret;
288 }
289 
virThreadSelf(virThread * thread)290 void virThreadSelf(virThread *thread)
291 {
292     thread->thread = pthread_self();
293 }
294 
virThreadIsSelf(virThread * thread)295 bool virThreadIsSelf(virThread *thread)
296 {
297     return pthread_equal(pthread_self(), thread->thread) ? true : false;
298 }
299 
300 /* For debugging use only; this result is not guaranteed unique if
301  * pthread_t is larger than a 64-bit pointer, nor does it always match
302  * the pthread_self() id on Linux.  */
virThreadSelfID(void)303 unsigned long long virThreadSelfID(void)
304 {
305 #if defined(WITH_SYS_SYSCALL_H) && defined(SYS_gettid) && defined(__linux__)
306     pid_t tid = syscall(SYS_gettid);
307     return tid;
308 #else
309     union {
310         unsigned long long l;
311         pthread_t t;
312     } u;
313     u.t = pthread_self();
314     return u.l;
315 #endif
316 }
317 
318 /* For debugging use only; this result is not guaranteed unique if
319  * pthread_t is larger than a 64-bit pointer, nor does it always match
320  * the thread id of virThreadSelfID on Linux.  */
virThreadID(virThread * thread)321 unsigned long long virThreadID(virThread *thread)
322 {
323     union {
324         unsigned long long l;
325         pthread_t t;
326     } u;
327     u.t = thread->thread;
328     return u.l;
329 }
330 
virThreadJoin(virThread * thread)331 void virThreadJoin(virThread *thread)
332 {
333     pthread_join(thread->thread, NULL);
334 }
335 
virThreadCancel(virThread * thread)336 void virThreadCancel(virThread *thread)
337 {
338     pthread_cancel(thread->thread);
339 }
340 
virThreadLocalInit(virThreadLocal * l,virThreadLocalCleanup c)341 int virThreadLocalInit(virThreadLocal *l,
342                        virThreadLocalCleanup c)
343 {
344     int ret;
345     if ((ret = pthread_key_create(&l->key, c)) != 0) {
346         errno = ret;
347         return -1;
348     }
349     return 0;
350 }
351 
virThreadLocalGet(virThreadLocal * l)352 void *virThreadLocalGet(virThreadLocal *l)
353 {
354     return pthread_getspecific(l->key);
355 }
356 
virThreadLocalSet(virThreadLocal * l,void * val)357 int virThreadLocalSet(virThreadLocal *l, void *val)
358 {
359     int err = pthread_setspecific(l->key, val);
360     if (err) {
361         errno = err;
362         return -1;
363     }
364     return 0;
365 }
366