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