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 "test.h"
25
26 #include "testutil.h"
27 #include "warnless.h"
28 #include "memdebug.h"
29
30 #ifdef HAVE_PTHREAD_H
31 #include <pthread.h>
32 #include <unistd.h>
33
34 #define TEST_HANG_TIMEOUT 60 * 1000
35 #define CONN_NUM 3
36 #define TIME_BETWEEN_START_SECS 2
37
38 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
39 static CURL *pending_handles[CONN_NUM];
40 static int pending_num = 0;
41 static int test_failure = 0;
42
43 static CURLM *multi = NULL;
44 static const char *url;
45
run_thread(void * ptr)46 static void *run_thread(void *ptr)
47 {
48 CURL *easy = NULL;
49 int res = 0;
50 int i;
51
52 (void)ptr;
53
54 for(i = 0; i < CONN_NUM; i++) {
55 wait_ms(TIME_BETWEEN_START_SECS * 1000);
56
57 easy_init(easy);
58
59 easy_setopt(easy, CURLOPT_URL, url);
60 easy_setopt(easy, CURLOPT_VERBOSE, 0L);
61
62 pthread_mutex_lock(&lock);
63
64 if(test_failure) {
65 pthread_mutex_unlock(&lock);
66 goto test_cleanup;
67 }
68
69 pending_handles[pending_num] = easy;
70 pending_num++;
71 easy = NULL;
72
73 pthread_mutex_unlock(&lock);
74
75 res_multi_wakeup(multi);
76 }
77
78 test_cleanup:
79
80 curl_easy_cleanup(easy);
81
82 pthread_mutex_lock(&lock);
83
84 if(!test_failure)
85 test_failure = res;
86
87 pthread_mutex_unlock(&lock);
88
89 return NULL;
90 }
91
test(char * URL)92 int test(char *URL)
93 {
94 int still_running;
95 int num;
96 int i;
97 int res = 0;
98 CURL *started_handles[CONN_NUM];
99 int started_num = 0;
100 int finished_num = 0;
101 pthread_t tid;
102 bool tid_valid = false;
103 struct CURLMsg *message;
104
105 start_test_timing();
106
107 global_init(CURL_GLOBAL_ALL);
108
109 multi_init(multi);
110
111 url = URL;
112
113 res = pthread_create(&tid, NULL, run_thread, NULL);
114 if(!res)
115 tid_valid = true;
116 else {
117 fprintf(stderr, "%s:%d Couldn't create thread, errno %d\n",
118 __FILE__, __LINE__, res);
119 goto test_cleanup;
120 }
121
122 while(1) {
123 multi_perform(multi, &still_running);
124
125 abort_on_test_timeout();
126
127 while((message = curl_multi_info_read(multi, &num))) {
128 if(message->msg == CURLMSG_DONE) {
129 res = message->data.result;
130 if(res)
131 goto test_cleanup;
132 multi_remove_handle(multi, message->easy_handle);
133 finished_num++;
134 }
135 else {
136 fprintf(stderr, "%s:%d Got an unexpected message from curl: %i\n",
137 __FILE__, __LINE__, (int)message->msg);
138 res = TEST_ERR_MAJOR_BAD;
139 goto test_cleanup;
140 }
141
142 abort_on_test_timeout();
143 }
144
145 if(CONN_NUM == finished_num)
146 break;
147
148 multi_poll(multi, NULL, 0, TEST_HANG_TIMEOUT, &num);
149
150 abort_on_test_timeout();
151
152 pthread_mutex_lock(&lock);
153
154 while(pending_num > 0) {
155 res_multi_add_handle(multi, pending_handles[pending_num - 1]);
156 if(res) {
157 pthread_mutex_unlock(&lock);
158 goto test_cleanup;
159 }
160
161 started_handles[started_num] = pending_handles[pending_num - 1];
162 started_num++;
163 pending_num--;
164 }
165
166 pthread_mutex_unlock(&lock);
167
168 abort_on_test_timeout();
169 }
170
171 if(CONN_NUM != started_num) {
172 fprintf(stderr, "%s:%d Not all connections started: %d of %d\n",
173 __FILE__, __LINE__, started_num, CONN_NUM);
174 goto test_cleanup;
175 }
176
177 if(CONN_NUM != finished_num) {
178 fprintf(stderr, "%s:%d Not all connections finished: %d of %d\n",
179 __FILE__, __LINE__, started_num, CONN_NUM);
180 goto test_cleanup;
181 }
182
183 test_cleanup:
184
185 pthread_mutex_lock(&lock);
186 if(!test_failure)
187 test_failure = res;
188 pthread_mutex_unlock(&lock);
189
190 if(tid_valid)
191 pthread_join(tid, NULL);
192
193 curl_multi_cleanup(multi);
194 for(i = 0; i < pending_num; i++)
195 curl_easy_cleanup(pending_handles[i]);
196 for(i = 0; i < started_num; i++)
197 curl_easy_cleanup(started_handles[i]);
198 curl_global_cleanup();
199
200 return test_failure;
201 }
202
203 #else /* without pthread, this test doesn't work */
test(char * URL)204 int test(char *URL)
205 {
206 (void)URL;
207 return 0;
208 }
209 #endif
210