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