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 CURLcode test_failure = CURLE_OK;
42
43 static CURLM *testmulti = NULL;
44 static const char *url;
45
run_thread(void * ptr)46 static void *run_thread(void *ptr)
47 {
48 CURL *easy = NULL;
49 CURLcode res = CURLE_OK;
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(testmulti);
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 CURLcode test(char *URL)
93 {
94 int still_running;
95 int num;
96 int i;
97 int result;
98 CURLcode res = CURLE_OK;
99 CURL *started_handles[CONN_NUM];
100 int started_num = 0;
101 int finished_num = 0;
102 pthread_t tid;
103 bool tid_valid = false;
104 struct CURLMsg *message;
105
106 start_test_timing();
107
108 global_init(CURL_GLOBAL_ALL);
109
110 multi_init(testmulti);
111
112 url = URL;
113
114 result = pthread_create(&tid, NULL, run_thread, NULL);
115 if(!result)
116 tid_valid = true;
117 else {
118 fprintf(stderr, "%s:%d Couldn't create thread, errno %d\n",
119 __FILE__, __LINE__, result);
120 goto test_cleanup;
121 }
122
123 while(1) {
124 multi_perform(testmulti, &still_running);
125
126 abort_on_test_timeout();
127
128 while((message = curl_multi_info_read(testmulti, &num))) {
129 if(message->msg == CURLMSG_DONE) {
130 res = message->data.result;
131 if(res)
132 goto test_cleanup;
133 multi_remove_handle(testmulti, message->easy_handle);
134 finished_num++;
135 }
136 else {
137 fprintf(stderr, "%s:%d Got an unexpected message from curl: %i\n",
138 __FILE__, __LINE__, (int)message->msg);
139 res = TEST_ERR_MAJOR_BAD;
140 goto test_cleanup;
141 }
142
143 abort_on_test_timeout();
144 }
145
146 if(CONN_NUM == finished_num)
147 break;
148
149 multi_poll(testmulti, NULL, 0, TEST_HANG_TIMEOUT, &num);
150
151 abort_on_test_timeout();
152
153 pthread_mutex_lock(&lock);
154
155 while(pending_num > 0) {
156 res_multi_add_handle(testmulti, pending_handles[pending_num - 1]);
157 if(res) {
158 pthread_mutex_unlock(&lock);
159 goto test_cleanup;
160 }
161
162 started_handles[started_num] = pending_handles[pending_num - 1];
163 started_num++;
164 pending_num--;
165 }
166
167 pthread_mutex_unlock(&lock);
168
169 abort_on_test_timeout();
170 }
171
172 if(CONN_NUM != started_num) {
173 fprintf(stderr, "%s:%d Not all connections started: %d of %d\n",
174 __FILE__, __LINE__, started_num, CONN_NUM);
175 goto test_cleanup;
176 }
177
178 if(CONN_NUM != finished_num) {
179 fprintf(stderr, "%s:%d Not all connections finished: %d of %d\n",
180 __FILE__, __LINE__, started_num, CONN_NUM);
181 goto test_cleanup;
182 }
183
184 test_cleanup:
185
186 pthread_mutex_lock(&lock);
187 if(!test_failure)
188 test_failure = res;
189 pthread_mutex_unlock(&lock);
190
191 if(tid_valid)
192 pthread_join(tid, NULL);
193
194 curl_multi_cleanup(testmulti);
195 for(i = 0; i < pending_num; i++)
196 curl_easy_cleanup(pending_handles[i]);
197 for(i = 0; i < started_num; i++)
198 curl_easy_cleanup(started_handles[i]);
199 curl_global_cleanup();
200
201 return test_failure;
202 }
203
204 #else /* without pthread, this test doesn't work */
test(char * URL)205 CURLcode test(char *URL)
206 {
207 (void)URL;
208 return 0;
209 }
210 #endif
211