Coverage Report

Created: 2025-03-18 06:55

/src/wget2/libwget/thread.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2013 Tim Ruehsen
3
 * Copyright (c) 2015-2024 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
 */
83
int wget_thread_mutex_init(wget_thread_mutex *mutex)
84
20.9k
{
85
20.9k
  *mutex = wget_malloc(sizeof(struct wget_thread_mutex_st));
86
87
20.9k
  if (!*mutex)
88
0
    return WGET_E_MEMORY;
89
90
20.9k
  return glthread_lock_init(&((*mutex)->mutex));
91
20.9k
}
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
 */
101
int wget_thread_mutex_destroy(wget_thread_mutex *mutex)
102
20.9k
{
103
20.9k
  int rc = glthread_lock_destroy(&(*mutex)->mutex);
104
20.9k
  xfree(*mutex);
105
20.9k
  return rc;
106
20.9k
}
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
 */
115
void wget_thread_mutex_lock(wget_thread_mutex mutex)
116
25.4k
{
117
25.4k
  glthread_lock_lock(&mutex->mutex);
118
25.4k
}
119
120
/**
121
 * \param[in] mutex The mutex to be unlocked
122
 *
123
 * Unlocks the \p mutex.
124
 */
125
void wget_thread_mutex_unlock(wget_thread_mutex mutex)
126
25.4k
{
127
25.4k
  glthread_lock_unlock(&mutex->mutex);
128
25.4k
}
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
 */
139
int wget_thread_cond_init(wget_thread_cond *cond)
140
0
{
141
0
  *cond = wget_malloc(sizeof(struct wget_thread_cond_st));
142
143
0
  if (!*cond)
144
0
    return WGET_E_MEMORY;
145
146
0
  return glthread_cond_init(&((*cond)->cond));
147
0
}
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
 */
157
int wget_thread_cond_destroy(wget_thread_cond *cond)
158
0
{
159
0
  int rc = glthread_cond_destroy(&(*cond)->cond);
160
0
  xfree(*cond);
161
0
  return rc;
162
0
}
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
 */
170
int wget_thread_cond_signal(wget_thread_cond cond)
171
0
{
172
0
  return glthread_cond_broadcast(&cond->cond);
173
0
}
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
 */
185
int wget_thread_cond_wait(wget_thread_cond cond, wget_thread_mutex mutex, long long ms)
186
0
{
187
0
  if (ms <= 0)
188
0
    return glthread_cond_wait(&cond->cond, &mutex->mutex);
189
190
  // pthread_cond_timedwait() wants an absolute time
191
0
  struct timespec ts;
192
0
  gettime(&ts);
193
0
  ms += ts.tv_sec * 1000LL + ts.tv_nsec / 1000000;
194
0
  ts.tv_sec = ms / 1000;
195
0
  ts.tv_nsec = (ms % 1000) * 1000000;
196
197
0
  return glthread_cond_timedwait(&cond->cond, &mutex->mutex, &ts);
198
0
}
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
 */
209
int wget_thread_start(wget_thread *thread, void *(*start_routine)(void *), void *arg, WGET_GCC_UNUSED int flags)
210
0
{
211
0
  if (wget_thread_support()) {
212
0
    *thread = wget_malloc(sizeof(struct wget_thread_st));
213
214
0
    if (!*thread)
215
0
      return WGET_E_MEMORY;
216
217
0
    return glthread_create(&((*thread)->tid), start_routine, arg);
218
0
  }
219
220
0
  *thread = NULL;
221
0
  start_routine(arg);
222
0
  return 0;
223
0
}
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
 */
231
int wget_thread_cancel(WGET_GCC_UNUSED wget_thread thread)
232
0
{
233
/*
234
  if (thread && thread->tid)
235
    return glthread_cancel(thread->tid);
236
237
  errno = ESRCH;
238
  return -1;
239
*/
240
0
  return 0;
241
0
}
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
 */
250
int wget_thread_kill(WGET_GCC_UNUSED wget_thread thread, WGET_GCC_UNUSED int sig)
251
0
{
252
/*  if (thread && thread->tid)
253
    return glthread_kill(thread->tid, sig);
254
255
  errno = ESRCH;
256
  return -1;
257
*/
258
0
  return 0;
259
0
}
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
 */
270
int wget_thread_join(wget_thread *thread)
271
0
{
272
0
  if (thread && *thread && (*thread)->tid) {
273
0
    int rc = glthread_join((*thread)->tid, NULL);
274
0
    xfree(*thread);
275
0
    return rc;
276
0
  }
277
278
0
  if (wget_thread_support()) {
279
0
    errno = ESRCH;
280
0
    return -1;
281
0
  }
282
283
0
  return 0;
284
0
}
285
286
/**
287
 * \return The thread id of the caller.
288
 *
289
 */
290
wget_thread_id wget_thread_self(void)
291
0
{
292
0
  return (wget_thread_id) gl_thread_self();
293
0
}
294
295
/**
296
 * \return Whether libwget supports multi-threading on this platform or not.
297
 */
298
bool wget_thread_support(void)
299
0
{
300
0
#if defined USE_POSIX_THREADS || defined USE_PTH_THREADS || defined USE_SOLARIS_THREADS || defined USE_WINDOWS_THREADS
301
0
  return true;
302
#else
303
  return false;
304
#endif
305
0
}
306
307
/**@}*/