1 /*
2 * libiio - Library for interfacing industrial I/O (IIO) devices
3 *
4 * Copyright (C) 2016 Analog Devices, Inc.
5 * Author: Paul Cercueil <paul.cercueil@analog.com>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 */
17
18 #include "thread-pool.h"
19
20 #include <errno.h>
21 #include <pthread.h>
22 #include <signal.h>
23 #include <stdint.h>
24 #include <stdlib.h>
25 #include <sys/eventfd.h>
26 #include <unistd.h>
27
28 /*
29 * This is used to make sure that all active threads have finished cleanup when
30 * a STOP event is received. We don't use pthread_join() since for most threads
31 * we are OK with them exiting asynchronously and there really is no place to
32 * call pthread_join() to free the thread's resources. We only need to
33 * synchronize the threads that are still active when the iiod is shutdown to
34 * give them a chance to release all resources, disable buffers etc, before
35 * iio_context_destroy() is called.
36 */
37
38 struct thread_pool {
39 pthread_mutex_t thread_count_lock;
40 pthread_cond_t thread_count_cond;
41 unsigned int thread_count;
42 int stop_fd;
43 };
44
45 struct thread_body_data {
46 struct thread_pool *pool;
47 void (*f)(struct thread_pool *, void *);
48 void *d;
49 };
50
thread_pool_thread_started(struct thread_pool * pool)51 static void thread_pool_thread_started(struct thread_pool *pool)
52 {
53 pthread_mutex_lock(&pool->thread_count_lock);
54 pool->thread_count++;
55 pthread_mutex_unlock(&pool->thread_count_lock);
56 }
57
thread_pool_thread_stopped(struct thread_pool * pool)58 static void thread_pool_thread_stopped(struct thread_pool *pool)
59 {
60 pthread_mutex_lock(&pool->thread_count_lock);
61 pool->thread_count--;
62 pthread_cond_signal(&pool->thread_count_cond);
63 pthread_mutex_unlock(&pool->thread_count_lock);
64 }
65
thread_body(void * d)66 static void * thread_body(void *d)
67 {
68 struct thread_body_data *pdata = d;
69
70 (*pdata->f)(pdata->pool, pdata->d);
71
72 thread_pool_thread_stopped(pdata->pool);
73 free(pdata);
74
75 return NULL;
76 }
77
thread_pool_add_thread(struct thread_pool * pool,void (* f)(struct thread_pool *,void *),void * d,const char * name)78 int thread_pool_add_thread(struct thread_pool *pool,
79 void (*f)(struct thread_pool *, void *),
80 void *d, const char *name)
81 {
82 struct thread_body_data *pdata;
83 sigset_t sigmask, oldsigmask;
84 pthread_attr_t attr;
85 pthread_t thd;
86 int ret;
87
88 pdata = malloc(sizeof(*pdata));
89 if (!pdata)
90 return -ENOMEM;
91
92 pdata->f = f;
93 pdata->d = d;
94 pdata->pool = pool;
95
96 sigfillset(&sigmask);
97 pthread_sigmask(SIG_BLOCK, &sigmask, &oldsigmask);
98
99 pthread_attr_init(&attr);
100 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
101
102 /* In order to avoid race conditions thread_pool_thread_started() must
103 * be called before the thread is created and
104 * thread_pool_thread_stopped() must be called right before leaving
105 * the thread. */
106 thread_pool_thread_started(pool);
107
108 ret = pthread_create(&thd, &attr, thread_body, pdata);
109 if (ret) {
110 free(pdata);
111 thread_pool_thread_stopped(pool);
112 } else {
113 #ifdef HAS_PTHREAD_SETNAME_NP
114 pthread_setname_np(thd, name);
115 #endif
116 }
117
118 pthread_attr_destroy(&attr);
119 pthread_sigmask(SIG_SETMASK, &oldsigmask, NULL);
120 return ret;
121 }
122
thread_pool_new(void)123 struct thread_pool * thread_pool_new(void)
124 {
125 struct thread_pool *pool;
126
127 pool = malloc(sizeof(*pool));
128 if (!pool) {
129 errno = ENOMEM;
130 return NULL;
131 }
132
133 pool->stop_fd = eventfd(0, EFD_NONBLOCK);
134 if (pool->stop_fd == -1) {
135 int err = errno;
136
137 free(pool);
138 errno = err;
139 return NULL;
140 }
141
142 pthread_mutex_init(&pool->thread_count_lock, NULL);
143 pthread_cond_init(&pool->thread_count_cond, NULL);
144 pool->thread_count = 0;
145
146 return pool;
147 }
148
thread_pool_get_poll_fd(const struct thread_pool * pool)149 int thread_pool_get_poll_fd(const struct thread_pool *pool)
150 {
151 return pool->stop_fd;
152 }
153
thread_pool_stop(struct thread_pool * pool)154 void thread_pool_stop(struct thread_pool *pool)
155 {
156 uint64_t e = 1;
157 int ret;
158
159 do {
160 ret = write(pool->stop_fd, &e, sizeof(e));
161 } while (ret == -1 && errno == EINTR);
162 }
163
thread_pool_stop_and_wait(struct thread_pool * pool)164 void thread_pool_stop_and_wait(struct thread_pool *pool)
165 {
166 uint64_t e;
167 int ret;
168
169 thread_pool_stop(pool);
170
171 pthread_mutex_lock(&pool->thread_count_lock);
172 while (pool->thread_count)
173 pthread_cond_wait(&pool->thread_count_cond,
174 &pool->thread_count_lock);
175 pthread_mutex_unlock(&pool->thread_count_lock);
176
177 do {
178 ret = read(pool->stop_fd, &e, sizeof(e));
179 } while (ret != -1 || errno == EINTR);
180 }
181
thread_pool_destroy(struct thread_pool * pool)182 void thread_pool_destroy(struct thread_pool *pool)
183 {
184 pthread_mutex_destroy(&pool->thread_count_lock);
185 pthread_cond_destroy(&pool->thread_count_cond);
186
187 close(pool->stop_fd);
188 free(pool);
189 }
190