1 /*
2  * Copyright (c) 2013 Tim Ruehsen
3  * Copyright (c) 2015-2021 Free Software Foundation, Inc.
4  *
5  * This file is part of libwget.
6  *
7  * Libwget is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Lesser General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * Libwget 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
15  * GNU Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with libwget.  If not, see <https://www.gnu.org/licenses/>.
19  *
20  *
21  * Thread wrapper routines
22  *
23  * Changelog
24  * 02.02.2013  Tim Ruehsen  created
25  *
26  */
27 
28 #include <config.h>
29 
30 #include <errno.h>
31 
32 // silence warnings in gnulib code
33 #if defined __clang__
34   #pragma clang diagnostic ignored "-Wundef"
35   #pragma clang diagnostic ignored "-Wshorten-64-to-32"
36   #pragma clang diagnostic ignored "-Wconditional-uninitialized"
37 #elif defined __GNUC__ && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
38   #pragma GCC diagnostic ignored "-Wundef"
39 #endif
40 
41 #include <glthread/thread.h>
42 #include <glthread/lock.h>
43 #include <glthread/cond.h>
44 
45 #include "timespec.h" // gnulib gettime()
46 
47 #include <wget.h>
48 #include "private.h"
49 
50 /**
51  * \file
52  * \brief Implementation of multi-threading basic functionality
53  * \defgroup libwget-thread Implementation of multi-threading basic functionality
54  * @{
55  *
56  * This is a wrapper around Gnulib's glthread functionality.
57  *
58  * It currently supports Posix threads (pthreads), GNU Pth threads,
59  * Solaris threads and Windows threads.
60  */
61 
62 struct wget_thread_st {
63 	gl_thread_t tid; //!< thread id
64 };
65 
66 struct wget_thread_mutex_st {
67 	gl_lock_t mutex; //!< mutex
68 };
69 
70 struct wget_thread_cond_st {
71 	gl_cond_t cond; //!< conditional
72 };
73 
74 /**
75  * \param[in,out] mutex The mutex to initialize
76  * \return 0 on success, non-zero on failure
77  *
78  * Initializes the \p mutex.
79  *
80  * After usage, a call to wget_thread_mutex_destroy() frees
81  * the allocated resources.
82  */
wget_thread_mutex_init(wget_thread_mutex * mutex)83 int wget_thread_mutex_init(wget_thread_mutex *mutex)
84 {
85 	*mutex = wget_malloc(sizeof(struct wget_thread_mutex_st));
86 
87 	if (!*mutex)
88 		return WGET_E_MEMORY;
89 
90 	return glthread_lock_init(&((*mutex)->mutex));
91 }
92 
93 /**
94  * \param[in,out] mutex The mutex to destroy
95  * \return 0 on success, non-zero on failure
96  *
97  * Free's the \p mutex and it's resources.
98  *
99  * After calling this function, the \p mutex cannot be used any more.
100  */
wget_thread_mutex_destroy(wget_thread_mutex * mutex)101 int wget_thread_mutex_destroy(wget_thread_mutex *mutex)
102 {
103 	int rc = glthread_lock_destroy(&(*mutex)->mutex);
104 	xfree(*mutex);
105 	return rc;
106 }
107 
108 /**
109  * \param[in] mutex The mutex to be locked
110  *
111  * Creates a lock on the \p mutex.
112  *
113  * To unlock the \p mutex, call wget_thread_mutex_unlock().
114  */
wget_thread_mutex_lock(wget_thread_mutex mutex)115 void wget_thread_mutex_lock(wget_thread_mutex mutex)
116 {
117 	glthread_lock_lock(&mutex->mutex);
118 }
119 
120 /**
121  * \param[in] mutex The mutex to be unlocked
122  *
123  * Unlocks the \p mutex.
124  */
wget_thread_mutex_unlock(wget_thread_mutex mutex)125 void wget_thread_mutex_unlock(wget_thread_mutex mutex)
126 {
127 	glthread_lock_unlock(&mutex->mutex);
128 }
129 
130 /**
131  * \param[in,out] cond The conditional to initialize
132  * \return 0 on success, non-zero on failure
133  *
134  * Initializes the conditional \p cond.
135  *
136  * After usage, a call to wget_thread_cond_destroy() frees
137  * the allocated resources.
138  */
wget_thread_cond_init(wget_thread_cond * cond)139 int wget_thread_cond_init(wget_thread_cond *cond)
140 {
141 	*cond = wget_malloc(sizeof(struct wget_thread_cond_st));
142 
143 	if (!*cond)
144 		return WGET_E_MEMORY;
145 
146 	return glthread_cond_init(&((*cond)->cond));
147 }
148 
149 /**
150  * \param[in,out] cond The conditional to destroy
151  * \return 0 on success, non-zero on failure
152  *
153  * Free's the conditional \p cond and it's resources.
154  *
155  * After calling this function, \p cond cannot be used any more.
156  */
wget_thread_cond_destroy(wget_thread_cond * cond)157 int wget_thread_cond_destroy(wget_thread_cond *cond)
158 {
159 	int rc = glthread_cond_destroy(&(*cond)->cond);
160 	xfree(*cond);
161 	return rc;
162 }
163 
164 /**
165  * \param[in] cond The conditional to signal a condition
166  * \return 0 on success, non-zero on failure
167  *
168  * Wakes up one (random) thread that waits on the conditional.
169  */
wget_thread_cond_signal(wget_thread_cond cond)170 int wget_thread_cond_signal(wget_thread_cond cond)
171 {
172 	return glthread_cond_broadcast(&cond->cond);
173 }
174 
175 /**
176  * \param[in] cond The conditional to wait for
177  * \param[in] mutex The mutex needed for thread-safety
178  * \param[in] ms The wait timeout in milliseconds
179  * \return 0 on success, non-zero on failure
180  *
181  * Waits for a condition with a max. timeout of \p ms milliseconds.
182  *
183  * To wait forever use a timeout lower or equal then 0.
184  */
wget_thread_cond_wait(wget_thread_cond cond,wget_thread_mutex mutex,long long ms)185 int wget_thread_cond_wait(wget_thread_cond cond, wget_thread_mutex mutex, long long ms)
186 {
187 	if (ms <= 0)
188 		return glthread_cond_wait(&cond->cond, &mutex->mutex);
189 
190 	// pthread_cond_timedwait() wants an absolute time
191 	struct timespec ts;
192 	gettime(&ts);
193 	ms += ts.tv_sec * 1000LL + ts.tv_nsec / 1000000;
194 	ts.tv_sec = ms / 1000;
195 	ts.tv_nsec = (ms % 1000) * 1000000;
196 
197 	return glthread_cond_timedwait(&cond->cond, &mutex->mutex, &ts);
198 }
199 
200 /**
201  * \param[out] thread The thread variable to be initialized
202  * \param[in] start_routine The thread function to start
203  * \param[in] arg The argument given to \p start_routine
204  * \param[in] flags Currently unused
205  * \return 0 on success, non-zero on failure
206  *
207  * Start \p start_routine as own thread with argument \p arg.
208  */
wget_thread_start(wget_thread * thread,void * (* start_routine)(void *),void * arg,WGET_GCC_UNUSED int flags)209 int wget_thread_start(wget_thread *thread, void *(*start_routine)(void *), void *arg, WGET_GCC_UNUSED int flags)
210 {
211 	if (wget_thread_support()) {
212 		*thread = wget_malloc(sizeof(struct wget_thread_st));
213 
214 		if (!*thread)
215 			return WGET_E_MEMORY;
216 
217 		return glthread_create(&((*thread)->tid), start_routine, arg);
218 	}
219 
220 	*thread = NULL;
221 	start_routine(arg);
222 	return 0;
223 }
224 
225 /**
226  * \param[in] thread Thread to cancel
227  * \return 0 on success, non-zero on failure
228  *
229  * Currently a no-op function, since it's not portable.
230  */
wget_thread_cancel(WGET_GCC_UNUSED wget_thread thread)231 int wget_thread_cancel(WGET_GCC_UNUSED wget_thread thread)
232 {
233 /*
234 	if (thread && thread->tid)
235 		return glthread_cancel(thread->tid);
236 
237 	errno = ESRCH;
238 	return -1;
239 */
240 	return 0;
241 }
242 
243 /**
244  * \param[in] thread Thread to send the signal to
245  * \param[in] sig Signal to send
246  * \return 0 on success, non-zero on failure
247  *
248  * Currently a no-op function, since it's not portable.
249  */
wget_thread_kill(WGET_GCC_UNUSED wget_thread thread,WGET_GCC_UNUSED int sig)250 int wget_thread_kill(WGET_GCC_UNUSED wget_thread thread, WGET_GCC_UNUSED int sig)
251 {
252 /*	if (thread && thread->tid)
253 		return glthread_kill(thread->tid, sig);
254 
255 	errno = ESRCH;
256 	return -1;
257 */
258 	return 0;
259 }
260 
261 /**
262  * \param[in] thread Thread to wait for
263  * \return 0 on success, non-zero on failure
264  *
265  * Wait until the \p thread has been stopped.
266  *
267  * This function just waits - to stop a thread you have take
268  * your own measurements.
269  */
wget_thread_join(wget_thread * thread)270 int wget_thread_join(wget_thread *thread)
271 {
272 	if (thread && *thread && (*thread)->tid) {
273 		int rc = glthread_join((*thread)->tid, NULL);
274 		xfree(*thread);
275 		return rc;
276 	}
277 
278 	if (wget_thread_support()) {
279 		errno = ESRCH;
280 		return -1;
281 	}
282 
283 	return 0;
284 }
285 
286 /**
287  * \return The thread id of the caller.
288  *
289  */
wget_thread_self(void)290 wget_thread_id wget_thread_self(void)
291 {
292 	return gl_thread_self();
293 }
294 
295 /**
296  * \return Whether libwget supports multi-threading on this platform or not.
297  */
wget_thread_support(void)298 bool wget_thread_support(void)
299 {
300 #if defined USE_POSIX_THREADS || defined USE_PTH_THREADS || defined USE_SOLARIS_THREADS || defined USE_WINDOWS_THREADS
301 	return true;
302 #else
303 	return false;
304 #endif
305 }
306 
307 /**@}*/
308