1 /*****************************************************************************
2  * linux/thread.c: Linux specifics for threading
3  *****************************************************************************
4  * Copyright (C) 2016 Rémi Denis-Courmont
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation; either version 2.1 of the License, or
9  * (at your option) any later version.
10  *
11  * This program 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
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19  *****************************************************************************/
20 
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24 
25 #include <errno.h>
26 #include <limits.h>
27 #include <stdlib.h>
28 #include <sys/types.h>
29 #include <unistd.h>
30 #include <sys/syscall.h>
31 #include <linux/futex.h>
32 
33 #ifndef FUTEX_PRIVATE_FLAG
34 #define FUTEX_WAKE_PRIVATE FUTEX_WAKE
35 #define FUTEX_WAIT_PRIVATE FUTEX_WAIT
36 #endif
37 
38 #include <vlc_common.h>
39 
vlc_thread_id(void)40 unsigned long vlc_thread_id(void)
41 {
42      static __thread pid_t tid = 0;
43 
44      if (unlikely(tid == 0))
45          tid = syscall(__NR_gettid);
46 
47      return tid;
48 }
49 
sys_futex(void * addr,int op,unsigned val,const struct timespec * to,void * addr2,int val3)50 static int sys_futex(void *addr, int op, unsigned val,
51                      const struct timespec *to, void *addr2, int val3)
52 {
53     return syscall(__NR_futex, addr, op, val, to, addr2, val3);
54 }
55 
vlc_futex_wake(void * addr,int nr)56 static int vlc_futex_wake(void *addr, int nr)
57 {
58     return sys_futex(addr, FUTEX_WAKE_PRIVATE, nr, NULL, NULL, 0);
59 }
60 
vlc_futex_wait(void * addr,unsigned val,const struct timespec * to)61 static int vlc_futex_wait(void *addr, unsigned val, const struct timespec *to)
62 {
63     return sys_futex(addr, FUTEX_WAIT_PRIVATE, val, to, NULL, 0);
64 }
65 
vlc_addr_signal(void * addr)66 void vlc_addr_signal(void *addr)
67 {
68     vlc_futex_wake(addr, 1);
69 }
70 
vlc_addr_broadcast(void * addr)71 void vlc_addr_broadcast(void *addr)
72 {
73     vlc_futex_wake(addr, INT_MAX);
74 }
75 
vlc_addr_wait(void * addr,unsigned val)76 void vlc_addr_wait(void *addr, unsigned val)
77 {
78     vlc_futex_wait(addr, val, NULL);
79 }
80 
vlc_addr_timedwait(void * addr,unsigned val,mtime_t delay)81 bool vlc_addr_timedwait(void *addr, unsigned val, mtime_t delay)
82 {
83     lldiv_t d = lldiv(delay, CLOCK_FREQ);
84     struct timespec ts = { d.quot, d.rem * (1000000000 / CLOCK_FREQ) };
85 
86     return (vlc_futex_wait(addr, val, &ts) == 0 || errno != ETIMEDOUT);
87 }
88