Coverage Report

Created: 2026-07-16 06:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl/lib/curl_threads.c
Line
Count
Source
1
/***************************************************************************
2
 *                                  _   _ ____  _
3
 *  Project                     ___| | | |  _ \| |
4
 *                             / __| | | | |_) | |
5
 *                            | (__| |_| |  _ <| |___
6
 *                             \___|\___/|_| \_\_____|
7
 *
8
 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9
 *
10
 * This software is licensed as described in the file COPYING, which
11
 * you should have received as part of this distribution. The terms
12
 * are also available at https://curl.se/docs/copyright.html.
13
 *
14
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15
 * copies of the Software, and permit persons to whom the Software is
16
 * furnished to do so, under the terms of the COPYING file.
17
 *
18
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19
 * KIND, either express or implied.
20
 *
21
 * SPDX-License-Identifier: curl
22
 *
23
 ***************************************************************************/
24
#include "curl_setup.h"
25
#include "curl_threads.h"
26
#include "curlx/timeval.h"
27
28
#ifdef USE_THREADS
29
30
#ifdef HAVE_THREADS_POSIX
31
32
struct Curl_actual_call {
33
  unsigned int (*func)(void *);
34
  void *arg;
35
};
36
37
static void *curl_thread_create_thunk(void *arg)
38
0
{
39
0
  struct Curl_actual_call *ac = arg;
40
0
  unsigned int (*func)(void *) = ac->func;
41
0
  void *real_arg = ac->arg;
42
43
0
  curlx_free(ac);
44
45
0
  (*func)(real_arg);
46
47
0
  return 0;
48
0
}
49
50
curl_thread_t Curl_thread_create(
51
  CURL_THREAD_RETURN_T(CURL_STDCALL *func)(void *), void *arg)
52
0
{
53
0
  curl_thread_t t = curlx_malloc(sizeof(pthread_t));
54
0
  struct Curl_actual_call *ac = NULL;
55
0
  int rc;
56
57
0
  if(t)
58
0
    ac = curlx_malloc(sizeof(struct Curl_actual_call));
59
0
  if(!(ac && t))
60
0
    goto err;
61
62
0
  ac->func = func;
63
0
  ac->arg = arg;
64
65
0
  rc = pthread_create(t, NULL, curl_thread_create_thunk, ac);
66
0
  if(rc) {
67
0
    errno = rc;
68
0
    goto err;
69
0
  }
70
71
0
  return t;
72
73
0
err:
74
0
  curlx_free(t);
75
0
  curlx_free(ac);
76
0
  return curl_thread_t_null;
77
0
}
78
79
void Curl_thread_destroy(curl_thread_t *hnd)
80
0
{
81
0
  if(*hnd != curl_thread_t_null) {
82
0
    pthread_detach(**hnd);
83
0
    curlx_free(*hnd);
84
0
    *hnd = curl_thread_t_null;
85
0
  }
86
0
}
87
88
int Curl_thread_join(curl_thread_t *hnd)
89
0
{
90
0
  int ret = (pthread_join(**hnd, NULL) == 0);
91
92
0
  curlx_free(*hnd);
93
0
  *hnd = curl_thread_t_null;
94
95
0
  return ret;
96
0
}
97
98
#elif defined(_WIN32)
99
100
curl_thread_t Curl_thread_create(
101
  CURL_THREAD_RETURN_T(CURL_STDCALL *func)(void *), void *arg)
102
{
103
  curl_thread_t t = CreateThread(NULL, 0, func, arg, 0, NULL);
104
  if(!t) {
105
    DWORD gle = GetLastError();
106
    /* !checksrc! disable ERRNOVAR 1 */
107
    errno = (gle == ERROR_ACCESS_DENIED ||
108
             gle == ERROR_NOT_ENOUGH_MEMORY) ?
109
             EACCES : EINVAL;
110
    return curl_thread_t_null;
111
  }
112
  return t;
113
}
114
115
void Curl_thread_destroy(curl_thread_t *hnd)
116
{
117
  if(*hnd != curl_thread_t_null) {
118
    CloseHandle(*hnd);
119
    *hnd = curl_thread_t_null;
120
  }
121
}
122
123
int Curl_thread_join(curl_thread_t *hnd)
124
{
125
  int ret = (WaitForSingleObjectEx(*hnd, INFINITE, FALSE) == WAIT_OBJECT_0);
126
127
  Curl_thread_destroy(hnd);
128
129
  return ret;
130
}
131
132
#else
133
#error neither HAVE_THREADS_POSIX nor _WIN32 defined
134
#endif
135
#endif /* USE_THREADS */
136
137
#ifdef USE_MUTEX
138
139
#ifdef HAVE_THREADS_POSIX
140
141
void Curl_cond_signal(pthread_cond_t *c)
142
0
{
143
  /* return code defined as always 0 */
144
0
  (void)pthread_cond_signal(c);
145
0
}
146
147
void Curl_cond_wait(pthread_cond_t *c, pthread_mutex_t *m)
148
0
{
149
  /* return code defined as always 0 */
150
0
  (void)pthread_cond_wait(c, m);
151
0
}
152
153
CURLcode Curl_cond_timedwait(pthread_cond_t *c, pthread_mutex_t *m,
154
                             uint32_t timeout_ms)
155
0
{
156
0
  struct curltime now;
157
0
  struct timespec ts;
158
0
  timediff_t usec;
159
0
  int rc;
160
161
  /* POSIX expects an "absolute" time until the condition wait ends.
162
   * We cannot use `curlx_now()` here that may run on some monotonic clock
163
   * that will be most likely in the past, as far as POSIX abstime is
164
   * concerned. */
165
0
#ifdef HAVE_GETTIMEOFDAY
166
0
  struct timeval tv;
167
0
  (void)gettimeofday(&tv, NULL);
168
0
  now.tv_sec = tv.tv_sec;
169
0
  now.tv_usec = (int)tv.tv_usec;
170
#else
171
  now.tv_sec = time(NULL);
172
  now.tv_usec = 0;
173
#endif
174
175
0
  ts.tv_sec = now.tv_sec + (timeout_ms / 1000);
176
0
  usec = now.tv_usec + ((timeout_ms % 1000) * 1000);
177
0
  if(usec >= 1000000) {
178
0
    ++ts.tv_sec;
179
0
    usec %= 1000000;
180
0
  }
181
0
  ts.tv_nsec = (long)usec * 1000;
182
183
0
  rc = pthread_cond_timedwait(c, m, &ts);
184
0
  if(rc == SOCKETIMEDOUT)
185
0
    return CURLE_OPERATION_TIMEDOUT;
186
0
  return rc ? CURLE_UNRECOVERABLE_POLL : CURLE_OK;
187
0
}
188
189
#elif defined(_WIN32)
190
191
void Curl_cond_signal(CONDITION_VARIABLE *c)
192
{
193
  WakeConditionVariable(c);
194
}
195
196
void Curl_cond_wait(CONDITION_VARIABLE *c, CRITICAL_SECTION *m)
197
{
198
  SleepConditionVariableCS(c, m, INFINITE);
199
}
200
201
CURLcode Curl_cond_timedwait(CONDITION_VARIABLE *c, CRITICAL_SECTION *m,
202
                             uint32_t timeout_ms)
203
{
204
  if(!SleepConditionVariableCS(c, m, (DWORD)timeout_ms)) {
205
    DWORD err = GetLastError();
206
    return (err == ERROR_TIMEOUT) ?
207
           CURLE_OPERATION_TIMEDOUT : CURLE_UNRECOVERABLE_POLL;
208
  }
209
  return CURLE_OK;
210
}
211
#else
212
#error neither HAVE_THREADS_POSIX nor _WIN32 defined
213
#endif
214
#endif /* USE_MUTEX */