xref: /curl/tests/libtest/lib3207.c (revision bc2f72b9)
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 #include "testutil.h"
26 #include "memdebug.h"
27 
28 #include <stdio.h>
29 
30 #if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32)
31 #if defined(USE_THREADS_POSIX)
32 #include <pthread.h>
33 #endif
34 #include "curl_threads.h"
35 #endif
36 
37 #define CAINFO libtest_arg2
38 #define THREAD_SIZE 16
39 #define PER_THREAD_SIZE 8
40 
41 struct Ctx
42 {
43   const char *URL;
44   CURLSH *share;
45   int result;
46   int thread_id;
47   struct curl_slist *contents;
48 };
49 
write_memory_callback(void * contents,size_t size,size_t nmemb,void * userp)50 static size_t write_memory_callback(void *contents, size_t size,
51   size_t nmemb, void *userp) {
52     /* append the data to contents */
53     size_t realsize = size * nmemb;
54     struct Ctx *mem = (struct Ctx *)userp;
55     char *data = (char *)malloc(realsize + 1);
56     struct curl_slist *item_append = NULL;
57     if(!data) {
58       printf("not enough memory (malloc returned NULL)\n");
59       return 0;
60     }
61     memcpy(data, contents, realsize);
62     data[realsize] = '\0';
63     item_append = curl_slist_append(mem->contents, data);
64     free(data);
65     if(item_append) {
66       mem->contents = item_append;
67     }
68     else {
69       printf("not enough memory (curl_slist_append returned NULL)\n");
70       return 0;
71     }
72     return realsize;
73 }
74 
75 static
76 #if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32)
77 #if defined(_WIN32_WCE) || defined(CURL_WINDOWS_UWP)
78 DWORD
79 #else
80 unsigned int
81 #endif
82 CURL_STDCALL
83 #else
84 unsigned int
85 #endif
test_thread(void * ptr)86 test_thread(void *ptr)
87 {
88   struct Ctx *ctx = (struct Ctx *)ptr;
89   CURLcode res = CURLE_OK;
90 
91   int i;
92 
93   /* Loop the transfer and cleanup the handle properly every lap. This will
94      still reuse ssl session since the pool is in the shared object! */
95   for(i = 0; i < PER_THREAD_SIZE; i++) {
96     CURL *curl = curl_easy_init();
97     if(curl) {
98       curl_easy_setopt(curl, CURLOPT_URL, (char *)ctx->URL);
99 
100       /* use the share object */
101       curl_easy_setopt(curl, CURLOPT_SHARE, ctx->share);
102       curl_easy_setopt(curl, CURLOPT_CAINFO, CAINFO);
103 
104       curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_memory_callback);
105       curl_easy_setopt(curl, CURLOPT_WRITEDATA, ptr);
106       curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
107 
108       /* Perform the request, res will get the return code */
109       res = curl_easy_perform(curl);
110 
111       /* always cleanup */
112       curl_easy_cleanup(curl);
113       /* Check for errors */
114       if(res != CURLE_OK) {
115         fprintf(stderr, "curl_easy_perform() failed: %s\n",
116                 curl_easy_strerror(res));
117         goto test_cleanup;
118       }
119     }
120   }
121 
122 test_cleanup:
123   ctx->result = (int)res;
124   return 0;
125 }
126 
127 #if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32)
128 
my_lock(CURL * handle,curl_lock_data data,curl_lock_access laccess,void * useptr)129 static void my_lock(CURL *handle, curl_lock_data data,
130                     curl_lock_access laccess, void *useptr)
131 {
132   curl_mutex_t *mutexes = (curl_mutex_t*) useptr;
133   (void)handle;
134   (void)laccess;
135   Curl_mutex_acquire(&mutexes[data]);
136 }
137 
my_unlock(CURL * handle,curl_lock_data data,void * useptr)138 static void my_unlock(CURL *handle, curl_lock_data data, void *useptr)
139 {
140   curl_mutex_t *mutexes = (curl_mutex_t*) useptr;
141   (void)handle;
142   Curl_mutex_release(&mutexes[data]);
143 }
144 
execute(struct Curl_share * share,struct Ctx * ctx)145 static void execute(struct Curl_share *share, struct Ctx *ctx)
146 {
147   int i;
148   curl_mutex_t mutexes[CURL_LOCK_DATA_LAST - 1];
149   curl_thread_t thread[THREAD_SIZE];
150   for(i = 0; i < CURL_LOCK_DATA_LAST - 1; i++) {
151     Curl_mutex_init(&mutexes[i]);
152   }
153   curl_share_setopt(share, CURLSHOPT_LOCKFUNC, my_lock);
154   curl_share_setopt(share, CURLSHOPT_UNLOCKFUNC, my_unlock);
155   curl_share_setopt(share, CURLSHOPT_USERDATA, (void *)mutexes);
156   curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
157 
158   for(i = 0; i < THREAD_SIZE; i++) {
159     thread[i] = Curl_thread_create(test_thread, (void *)&ctx[i]);
160   }
161   for(i = 0; i < THREAD_SIZE; i++) {
162     if(thread[i]) {
163       Curl_thread_join(&thread[i]);
164       Curl_thread_destroy(thread[i]);
165     }
166   }
167   curl_share_setopt(share, CURLSHOPT_LOCKFUNC, NULL);
168   curl_share_setopt(share, CURLSHOPT_UNLOCKFUNC, NULL);
169   for(i = 0; i < CURL_LOCK_DATA_LAST - 1; i++) {
170     Curl_mutex_destroy(&mutexes[i]);
171   }
172 }
173 
174 #else /* without pthread, run serially */
175 
execute(struct Curl_share * share,struct Ctx * ctx)176 static void execute(struct Curl_share *share, struct Ctx *ctx)
177 {
178   int i;
179   (void) share;
180   for(i = 0; i < THREAD_SIZE; i++) {
181     test_thread((void *)&ctx[i]);
182   }
183 }
184 
185 #endif
186 
test(char * URL)187 CURLcode test(char *URL)
188 {
189   int res = 0;
190   int i;
191   CURLSH* share;
192   struct Ctx ctx[THREAD_SIZE];
193 
194   curl_global_init(CURL_GLOBAL_ALL);
195 
196   share = curl_share_init();
197   if(!share) {
198     fprintf(stderr, "curl_share_init() failed\n");
199     goto test_cleanup;
200   }
201 
202   for(i = 0; i < THREAD_SIZE; i++) {
203     ctx[i].share = share;
204     ctx[i].URL = URL;
205     ctx[i].thread_id = i;
206     ctx[i].result = 0;
207     ctx[i].contents = NULL;
208   }
209 
210   execute(share, ctx);
211 
212   for(i = 0; i < THREAD_SIZE; i++) {
213     if(ctx[i].result) {
214       res = ctx[i].result;
215     }
216     else {
217         struct curl_slist *item = ctx[i].contents;
218         while(item) {
219           printf("%s", item->data);
220           item = item->next;
221         }
222     }
223     curl_slist_free_all(ctx[i].contents);
224   }
225 
226 test_cleanup:
227   if(share)
228     curl_share_cleanup(share);
229   curl_global_cleanup();
230   return (CURLcode)res;
231 }
232