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