/*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | * / __| | | | |_) | | * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * * Copyright (C) 1998 - 2019, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at https://curl.haxx.se/docs/copyright.html. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ***************************************************************************/ #include "test.h" #include "testutil.h" #include "warnless.h" #include "memdebug.h" #ifdef HAVE_PTHREAD_H #include #include #define TEST_HANG_TIMEOUT 60 * 1000 #define CONN_NUM 3 #define TIME_BETWEEN_START_SECS 2 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; static CURL *pending_handles[CONN_NUM]; static int pending_num = 0; static int test_failure = 0; static CURLM *multi = NULL; static const char *url; static void *run_thread(void *ptr) { CURL *easy = NULL; int res = 0; int i; (void)ptr; for(i = 0; i < CONN_NUM; i++) { sleep(TIME_BETWEEN_START_SECS); easy_init(easy); easy_setopt(easy, CURLOPT_URL, url); easy_setopt(easy, CURLOPT_VERBOSE, 0L); pthread_mutex_lock(&lock); if(test_failure) { pthread_mutex_unlock(&lock); goto test_cleanup; } pending_handles[pending_num] = easy; pending_num++; easy = NULL; pthread_mutex_unlock(&lock); multi_wakeup(multi); } test_cleanup: curl_easy_cleanup(easy); pthread_mutex_lock(&lock); if(!test_failure) test_failure = res; pthread_mutex_unlock(&lock); return NULL; } int test(char *URL) { int still_running; int num; int i; int res = 0; CURL *started_handles[CONN_NUM]; int started_num = 0; int finished_num = 0; pthread_t tid = 0; struct CURLMsg *message; start_test_timing(); global_init(CURL_GLOBAL_ALL); multi_init(multi); url = URL; res = pthread_create(&tid, NULL, run_thread, NULL); if(0 != res) { fprintf(stderr, "%s:%d Couldn't create thread, errno %d\n", __FILE__, __LINE__, res); goto test_cleanup; } while(1) { multi_perform(multi, &still_running); abort_on_test_timeout(); while((message = curl_multi_info_read(multi, &num)) != NULL) { if(message->msg == CURLMSG_DONE) { res = message->data.result; if(res) goto test_cleanup; multi_remove_handle(multi, message->easy_handle); finished_num++; } else { fprintf(stderr, "%s:%d Got an unexpected message from curl: %i\n", __FILE__, __LINE__, (int)message->msg); res = TEST_ERR_MAJOR_BAD; goto test_cleanup; } abort_on_test_timeout(); } if(CONN_NUM == finished_num) break; multi_poll(multi, NULL, 0, TEST_HANG_TIMEOUT, &num); abort_on_test_timeout(); pthread_mutex_lock(&lock); while(pending_num > 0) { res_multi_add_handle(multi, pending_handles[pending_num - 1]); if(res) { pthread_mutex_unlock(&lock); goto test_cleanup; } started_handles[started_num] = pending_handles[pending_num - 1]; started_num++; pending_num--; } pthread_mutex_unlock(&lock); abort_on_test_timeout(); } if(CONN_NUM != started_num) { fprintf(stderr, "%s:%d Not all connections started: %d of %d\n", __FILE__, __LINE__, started_num, CONN_NUM); goto test_cleanup; } if(CONN_NUM != finished_num) { fprintf(stderr, "%s:%d Not all connections finished: %d of %d\n", __FILE__, __LINE__, started_num, CONN_NUM); goto test_cleanup; } test_cleanup: pthread_mutex_lock(&lock); if(!test_failure) test_failure = res; pthread_mutex_unlock(&lock); if(0 != tid) pthread_join(tid, NULL); curl_multi_cleanup(multi); for(i = 0; i < pending_num; i++) curl_easy_cleanup(pending_handles[i]); for(i = 0; i < started_num; i++) curl_easy_cleanup(started_handles[i]); curl_global_cleanup(); return test_failure; } #else /* without pthread, this test doesn't work */ int test(char *URL) { (void)URL; return 0; } #endif