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