1 #define __USE_GNU
2 #include "tvheadend.h"
3 #include <fcntl.h>
4 #include <sys/types.h> /* See NOTES */
5 #include <sys/socket.h>
6 #include <sys/stat.h>
7 #include <sys/resource.h>
8 #include <unistd.h>
9 #include <signal.h>
10 #include <pthread.h>
11
12 #ifdef PLATFORM_LINUX
13 #include <sys/prctl.h>
14 #include <sys/syscall.h>
15 #endif
16
17 #ifdef PLATFORM_FREEBSD
18 #include <pthread_np.h>
19 #endif
20
21 /*
22 * filedescriptor routines
23 */
24
25 int
tvh_open(const char * pathname,int flags,mode_t mode)26 tvh_open(const char *pathname, int flags, mode_t mode)
27 {
28 int fd;
29
30 pthread_mutex_lock(&fork_lock);
31 fd = open(pathname, flags, mode);
32 if (fd != -1)
33 fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
34 pthread_mutex_unlock(&fork_lock);
35 return fd;
36 }
37
38 int
tvh_socket(int domain,int type,int protocol)39 tvh_socket(int domain, int type, int protocol)
40 {
41 int fd;
42
43 pthread_mutex_lock(&fork_lock);
44 fd = socket(domain, type, protocol);
45 if (fd != -1)
46 fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
47 pthread_mutex_unlock(&fork_lock);
48 return fd;
49 }
50
51 int
tvh_pipe(int flags,th_pipe_t * p)52 tvh_pipe(int flags, th_pipe_t *p)
53 {
54 int fd[2], err;
55 pthread_mutex_lock(&fork_lock);
56 err = pipe(fd);
57 if (err != -1) {
58 fcntl(fd[0], F_SETFD, fcntl(fd[0], F_GETFD) | FD_CLOEXEC);
59 fcntl(fd[1], F_SETFD, fcntl(fd[1], F_GETFD) | FD_CLOEXEC);
60 fcntl(fd[0], F_SETFL, fcntl(fd[0], F_GETFL) | flags);
61 fcntl(fd[1], F_SETFL, fcntl(fd[1], F_GETFL) | flags);
62 p->rd = fd[0];
63 p->wr = fd[1];
64 }
65 pthread_mutex_unlock(&fork_lock);
66 return err;
67 }
68
69 void
tvh_pipe_close(th_pipe_t * p)70 tvh_pipe_close(th_pipe_t *p)
71 {
72 close(p->rd);
73 close(p->wr);
74 p->rd = -1;
75 p->wr = -1;
76 }
77
78 int
tvh_write(int fd,const void * buf,size_t len)79 tvh_write(int fd, const void *buf, size_t len)
80 {
81 int64_t limit = mclk() + sec2mono(25);
82 ssize_t c;
83
84 while (len) {
85 c = write(fd, buf, len);
86 if (c < 0) {
87 if (ERRNO_AGAIN(errno)) {
88 if (mclk() > limit)
89 break;
90 tvh_safe_usleep(100);
91 continue;
92 }
93 break;
94 }
95 len -= c;
96 buf += c;
97 }
98
99 return len ? 1 : 0;
100 }
101
102 FILE *
tvh_fopen(const char * filename,const char * mode)103 tvh_fopen(const char *filename, const char *mode)
104 {
105 FILE *f;
106 int fd;
107 pthread_mutex_lock(&fork_lock);
108 f = fopen(filename, mode);
109 if (f) {
110 fd = fileno(f);
111 fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
112 }
113 pthread_mutex_unlock(&fork_lock);
114 return f;
115 }
116
117 /*
118 * thread routines
119 */
120
doquit(int sig)121 static void doquit(int sig)
122 {
123 }
124
125 struct
126 thread_state {
127 void *(*run)(void*);
128 void *arg;
129 char name[17];
130 };
131
132 static void *
thread_wrapper(void * p)133 thread_wrapper ( void *p )
134 {
135 struct thread_state *ts = p;
136 sigset_t set;
137
138 #if defined(PLATFORM_LINUX)
139 /* Set name */
140 prctl(PR_SET_NAME, ts->name);
141 #elif defined(PLATFORM_FREEBSD)
142 /* Set name of thread */
143 pthread_set_name_np(pthread_self(), ts->name);
144 #elif defined(PLATFORM_DARWIN)
145 pthread_setname_np(ts->name);
146 #endif
147
148 sigemptyset(&set);
149 sigaddset(&set, SIGTERM);
150 sigaddset(&set, SIGQUIT);
151 pthread_sigmask(SIG_UNBLOCK, &set, NULL);
152
153 signal(SIGTERM, doexit);
154 signal(SIGQUIT, doquit);
155
156 /* Run */
157 tvhtrace(LS_THREAD, "created thread %ld [%s / %p(%p)]",
158 (long)pthread_self(), ts->name, ts->run, ts->arg);
159 void *r = ts->run(ts->arg);
160 free(ts);
161
162 return r;
163 }
164
165 int
tvhthread_create(pthread_t * thread,const pthread_attr_t * attr,void * (* start_routine)(void *),void * arg,const char * name)166 tvhthread_create
167 (pthread_t *thread, const pthread_attr_t *attr,
168 void *(*start_routine) (void *), void *arg, const char *name)
169 {
170 int r;
171 struct thread_state *ts = calloc(1, sizeof(struct thread_state));
172 pthread_attr_t _attr;
173 if (attr == NULL) {
174 pthread_attr_init(&_attr);
175 pthread_attr_setstacksize(&_attr, 2*1024*1024);
176 attr = &_attr;
177 }
178 strlcpy(ts->name, "tvh:", 5);
179 strlcpy(ts->name+4, name, sizeof(ts->name)-4);
180 ts->run = start_routine;
181 ts->arg = arg;
182 r = pthread_create(thread, attr, thread_wrapper, ts);
183 return r;
184 }
185
186 /* linux style: -19 .. 20 */
187 int
tvhthread_renice(int value)188 tvhthread_renice(int value)
189 {
190 int ret = 0;
191 #ifdef SYS_gettid
192 pid_t tid;
193 tid = syscall(SYS_gettid);
194 ret = setpriority(PRIO_PROCESS, tid, value);
195 #elif ENABLE_ANDROID
196 pid_t tid;
197 tid = gettid();
198 ret = setpriority(PRIO_PROCESS, tid, value);
199 #elif defined(PLATFORM_DARWIN)
200 /* Currently not possible */
201 #elif defined(PLATFORM_FREEBSD)
202 /* Currently not possible */
203 #else
204 #warning "Implement renice for your platform!"
205 #endif
206 return ret;
207 }
208
209 int
tvh_mutex_timedlock(pthread_mutex_t * mutex,int64_t usec)210 tvh_mutex_timedlock
211 ( pthread_mutex_t *mutex, int64_t usec )
212 {
213 int64_t finish = getfastmonoclock() + usec;
214 int retcode;
215
216 while ((retcode = pthread_mutex_trylock (mutex)) == EBUSY) {
217 if (getfastmonoclock() >= finish)
218 return ETIMEDOUT;
219
220 tvh_safe_usleep(10000);
221 }
222
223 return retcode;
224 }
225
226 /*
227 * thread condition variables - monotonic clocks
228 */
229
230 int
tvh_cond_init(tvh_cond_t * cond)231 tvh_cond_init
232 ( tvh_cond_t *cond )
233 {
234 int r;
235
236 pthread_condattr_t attr;
237 pthread_condattr_init(&attr);
238 #if defined(PLATFORM_DARWIN)
239 /*
240 * pthread_condattr_setclock() not supported on platform Darwin.
241 * We use pthread_cond_timedwait_relative_np() which doesn't
242 * need it.
243 */
244 r = 0;
245 #else
246 r = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
247 if (r) {
248 fprintf(stderr, "Unable to set monotonic clocks for conditions! (%d)", r);
249 abort();
250 }
251 #endif
252 return pthread_cond_init(&cond->cond, &attr);
253 }
254
255 int
tvh_cond_destroy(tvh_cond_t * cond)256 tvh_cond_destroy
257 ( tvh_cond_t *cond )
258 {
259 return pthread_cond_destroy(&cond->cond);
260 }
261
262 int
tvh_cond_signal(tvh_cond_t * cond,int broadcast)263 tvh_cond_signal
264 ( tvh_cond_t *cond, int broadcast )
265 {
266 if (broadcast)
267 return pthread_cond_broadcast(&cond->cond);
268 else
269 return pthread_cond_signal(&cond->cond);
270 }
271
272 int
tvh_cond_wait(tvh_cond_t * cond,pthread_mutex_t * mutex)273 tvh_cond_wait
274 ( tvh_cond_t *cond, pthread_mutex_t *mutex)
275 {
276 return pthread_cond_wait(&cond->cond, mutex);
277 }
278
279 int
tvh_cond_timedwait(tvh_cond_t * cond,pthread_mutex_t * mutex,int64_t monoclock)280 tvh_cond_timedwait
281 ( tvh_cond_t *cond, pthread_mutex_t *mutex, int64_t monoclock )
282 {
283 #if defined(PLATFORM_DARWIN)
284 /* Use a relative timedwait implementation */
285 int64_t now = getmonoclock();
286 int64_t relative = monoclock - now;
287
288 struct timespec ts;
289 ts.tv_sec = relative / MONOCLOCK_RESOLUTION;
290 ts.tv_nsec = (relative % MONOCLOCK_RESOLUTION) *
291 (1000000000ULL/MONOCLOCK_RESOLUTION);
292
293 return pthread_cond_timedwait_relative_np(&cond->cond, mutex, &ts);
294 #else
295 struct timespec ts;
296 ts.tv_sec = monoclock / MONOCLOCK_RESOLUTION;
297 ts.tv_nsec = (monoclock % MONOCLOCK_RESOLUTION) *
298 (1000000000ULL/MONOCLOCK_RESOLUTION);
299 return pthread_cond_timedwait(&cond->cond, mutex, &ts);
300 #endif
301 }
302
303 /*
304 * clocks
305 */
306
307 void
tvh_safe_usleep(int64_t us)308 tvh_safe_usleep(int64_t us)
309 {
310 int64_t r;
311 if (us <= 0)
312 return;
313 do {
314 r = tvh_usleep(us);
315 if (r < 0) {
316 if (ERRNO_AGAIN(-r))
317 continue;
318 break;
319 }
320 us = r;
321 } while (r > 0);
322 }
323
324 int64_t
tvh_usleep(int64_t us)325 tvh_usleep(int64_t us)
326 {
327 #if defined(PLATFORM_DARWIN) || defined(PLATFORM_FREEBSD)
328 return usleep(us);
329 #else
330 struct timespec ts;
331 int64_t val;
332 int r;
333 if (us <= 0)
334 return 0;
335 ts.tv_sec = us / 1000000LL;
336 ts.tv_nsec = (us % 1000000LL) * 1000LL;
337 r = clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, &ts);
338 val = (ts.tv_sec * 1000000LL) + ((ts.tv_nsec + 500LL) / 1000LL);
339 if (ERRNO_AGAIN(r))
340 return val;
341 return r ? -r : 0;
342 #endif
343 }
344
345 int64_t
tvh_usleep_abs(int64_t us)346 tvh_usleep_abs(int64_t us)
347 {
348 #if defined(PLATFORM_DARWIN) || defined(PLATFORM_FREEBSD)
349 /* Convert to relative wait */
350 int64_t now = getmonoclock();
351 int64_t relative = us - now;
352 return tvh_usleep(relative);
353 #else
354 struct timespec ts;
355 int64_t val;
356 int r;
357 if (us <= 0)
358 return 0;
359 ts.tv_sec = us / 1000000LL;
360 ts.tv_nsec = (us % 1000000LL) * 1000LL;
361 r = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, &ts);
362 val = (ts.tv_sec * 1000000LL) + ((ts.tv_nsec + 500LL) / 1000LL);
363 if (ERRNO_AGAIN(r))
364 return val;
365 return r ? -r : 0;
366 #endif
367 }
368
369 /*
370 * qsort
371 */
372
373 #if ! ENABLE_QSORT_R
374 /*
375 * qsort_r wrapper for pre GLIBC 2.8
376 */
377
378 static __thread struct {
379 int (*cmp) ( const void *a, const void *b, void *p );
380 void *aux;
381 } qsort_r_data;
382
383 static int
qsort_r_wrap(const void * a,const void * b)384 qsort_r_wrap ( const void *a, const void *b )
385 {
386 return qsort_r_data.cmp(a, b, qsort_r_data.aux);
387 }
388
389 void
qsort_r(void * base,size_t nmemb,size_t size,int (* cmp)(const void *,const void *,void *),void * aux)390 qsort_r(void *base, size_t nmemb, size_t size,
391 int (*cmp)(const void *, const void *, void *), void *aux)
392 {
393 qsort_r_data.cmp = cmp;
394 qsort_r_data.aux = aux;
395 qsort(base, nmemb, size, qsort_r_wrap);
396 }
397 #endif /* ENABLE_QSORT_R */
398
399
400 #if defined(PLATFORM_FREEBSD) || defined(PLATFORM_DARWIN)
401 struct tvh_qsort_data {
402 void *arg;
403 int (*compar)(const void *, const void *, void *);
404 };
405
406
407 static int
tvh_qsort_swap(void * arg,const void * a,const void * b)408 tvh_qsort_swap(void *arg, const void *a, const void *b)
409 {
410 struct tvh_qsort_data *data = arg;
411 return data->compar(a, b, data->arg);
412 }
413 #endif /* PLATFORM_FREEBSD || PLATFORM_DARWIN */
414
415
416 void
tvh_qsort_r(void * base,size_t nmemb,size_t size,int (* compar)(const void *,const void *,void *),void * arg)417 tvh_qsort_r(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *, void *), void *arg)
418 {
419 #if defined(PLATFORM_FREEBSD) || defined(PLATFORM_DARWIN)
420 struct tvh_qsort_data swap_arg = {arg, compar};
421 qsort_r(base, nmemb, size, &swap_arg, tvh_qsort_swap);
422 #else
423 qsort_r(base, nmemb, size, compar, arg);
424 #endif
425 }
426