Line | Count | Source |
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 "curl_setup.h" |
25 | | |
26 | | #include "urldata.h" |
27 | | #include "api.h" |
28 | | #include "curl_threads.h" |
29 | | #include "multiif.h" |
30 | | #include "vtls/vtls_scache.h" |
31 | | |
32 | | struct Curl_eapi_fn_props { |
33 | | Curl_eapi_fn fn; |
34 | | uint8_t data_is_killed; /* easy handle is killed in call */ |
35 | | uint8_t recurse; /* may be called when another call is in progress */ |
36 | | uint8_t no_event_cb; /* may not be called during a multi event callback */ |
37 | | uint8_t no_scache_lock; /* may not be called with easy's vtls_scache |
38 | | locked by current thread */ |
39 | | }; |
40 | | |
41 | | static const struct Curl_eapi_fn_props eapi_fn_props[CURL_EAPI_FN_LAST] = { |
42 | | /* function kill rec !ev !scach */ |
43 | | { CURL_EAPI_FN_easy_cleanup, 1, 0, 0, 0 }, |
44 | | { CURL_EAPI_FN_easy_duphandle, 0, 1, 0, 0 }, |
45 | | { CURL_EAPI_FN_easy_getinfo, 0, 1, 0, 0 }, |
46 | | { CURL_EAPI_FN_easy_header, 0, 1, 0, 0 }, |
47 | | { CURL_EAPI_FN_easy_nextheader, 0, 1, 0, 0 }, |
48 | | { CURL_EAPI_FN_easy_pause, 0, 1, 1, 0 }, |
49 | | { CURL_EAPI_FN_easy_perform_ev, 0, 0, 0, 1 }, |
50 | | { CURL_EAPI_FN_easy_perform, 0, 0, 0, 1 }, |
51 | | { CURL_EAPI_FN_easy_recv, 0, 0, 0, 1 }, |
52 | | { CURL_EAPI_FN_easy_reset, 0, 0, 0, 0 }, |
53 | | { CURL_EAPI_FN_easy_send, 0, 0, 0, 1 }, |
54 | | { CURL_EAPI_FN_easy_setopt, 0, 1, 0, 0 }, |
55 | | { CURL_EAPI_FN_easy_ssls_export, 0, 0, 0, 1 }, |
56 | | { CURL_EAPI_FN_easy_ssls_import, 0, 0, 0, 1 }, |
57 | | { CURL_EAPI_FN_easy_upkeep, 0, 0, 0, 1 }, |
58 | | { CURL_EAPI_FN_ws_recv, 0, 1, 0, 1 }, |
59 | | { CURL_EAPI_FN_ws_send, 0, 1, 0, 1 }, |
60 | | { CURL_EAPI_FN_ws_start_frame, 0, 1, 0, 0 }, |
61 | | }; |
62 | | |
63 | | struct Curl_mapi_fn_props { |
64 | | Curl_mapi_fn fn; |
65 | | uint8_t multi_is_killed; /* multi handle is killed during call */ |
66 | | uint8_t recurse; /* may be called when another call is in progress */ |
67 | | uint8_t allow_ntfy_cb; /* may be called during a notify callback */ |
68 | | uint8_t no_scache_lock; /* may not be called with multi's vtls_scache |
69 | | locked by current thread */ |
70 | | }; |
71 | | |
72 | | static const struct Curl_mapi_fn_props mapi_fn_props[CURL_MAPI_FN_LAST] = { |
73 | | /* function kill rec ntfy !scach */ |
74 | | { CURL_MAPI_FN_multi_add_handle, 0, 0, 1, 0 }, |
75 | | { CURL_MAPI_FN_multi_assign, 0, 1, 1, 0 }, |
76 | | { CURL_MAPI_FN_multi_cleanup, 1, 0, 0, 1 }, |
77 | | { CURL_MAPI_FN_multi_fdset, 0, 0, 1, 0 }, |
78 | | { CURL_MAPI_FN_multi_get_handles, 0, 1, 1, 0 }, |
79 | | { CURL_MAPI_FN_multi_get_offt, 0, 1, 1, 0 }, |
80 | | { CURL_MAPI_FN_multi_info_read, 0, 1, 1, 0 }, |
81 | | { CURL_MAPI_FN_multi_notify_disable, 0, 1, 1, 0 }, |
82 | | { CURL_MAPI_FN_multi_notify_enable, 0, 1, 1, 0 }, |
83 | | { CURL_MAPI_FN_multi_perform, 0, 0, 0, 1 }, |
84 | | { CURL_MAPI_FN_multi_poll, 0, 0, 1, 0 }, |
85 | | { CURL_MAPI_FN_multi_remove_handle, 0, 0, 1, 0 }, |
86 | | { CURL_MAPI_FN_multi_setopt, 0, 0, 1, 0 }, |
87 | | { CURL_MAPI_FN_multi_socket_action, 0, 0, 0, 1 }, |
88 | | { CURL_MAPI_FN_multi_socket_all, 0, 0, 0, 1 }, |
89 | | { CURL_MAPI_FN_multi_socket, 0, 0, 0, 1 }, |
90 | | { CURL_MAPI_FN_multi_timeout, 0, 0, 1, 1 }, |
91 | | { CURL_MAPI_FN_multi_wait, 0, 0, 1, 0 }, |
92 | | { CURL_MAPI_FN_multi_waitfds, 0, 0, 1, 0 }, |
93 | | }; |
94 | | |
95 | | struct Curl_cbapi_fn_props { |
96 | | Curl_cbapi_fn fn; |
97 | | uint8_t is_event_cb; /* is a multi event processing callback */ |
98 | | }; |
99 | | |
100 | | static const struct Curl_cbapi_fn_props |
101 | | cbapi_fn_props[CURL_CBAPI_FN_LAST - CURL_CBAPI_FN_START] = { |
102 | | { CURL_CBAPI_FN_easy_chunk_bgn, 0 }, |
103 | | { CURL_CBAPI_FN_easy_chunk_end, 0 }, |
104 | | { CURL_CBAPI_FN_easy_closesocket, 0 }, |
105 | | { CURL_CBAPI_FN_easy_cr_in_read, 0 }, |
106 | | { CURL_CBAPI_FN_easy_cr_in_resume_from, 0 }, |
107 | | { CURL_CBAPI_FN_easy_cw_out_cb, 0 }, |
108 | | { CURL_CBAPI_FN_easy_fdebug, 0 }, |
109 | | { CURL_CBAPI_FN_easy_fnmatch_data, 0 }, |
110 | | { CURL_CBAPI_FN_easy_fopensocket, 0 }, |
111 | | { CURL_CBAPI_FN_easy_fprereq, 0 }, |
112 | | { CURL_CBAPI_FN_easy_fprogress, 0 }, |
113 | | { CURL_CBAPI_FN_easy_fread_func, 0 }, |
114 | | { CURL_CBAPI_FN_easy_fsockopt, 0 }, |
115 | | { CURL_CBAPI_FN_easy_fsslctx, 0 }, |
116 | | { CURL_CBAPI_FN_easy_fwrite_rtp, 0 }, |
117 | | { CURL_CBAPI_FN_easy_fxferinfo, 0 }, |
118 | | { CURL_CBAPI_FN_easy_ioctl_func, 0 }, |
119 | | { CURL_CBAPI_FN_easy_resolver_start, 0 }, |
120 | | { CURL_CBAPI_FN_easy_seek_func, 0 }, |
121 | | { CURL_CBAPI_FN_easy_ssh_hostkeyfunc, 0 }, |
122 | | { CURL_CBAPI_FN_easy_ssh_keyfunc, 0 }, |
123 | | { CURL_CBAPI_FN_easy_trailer_callback, 0 }, |
124 | | |
125 | | { CURL_CBAPI_FN_multi_ntfy_cb, 0 }, |
126 | | { CURL_CBAPI_FN_multi_push_cb, 0 }, |
127 | | { CURL_CBAPI_FN_multi_socket_cb, 1 }, |
128 | | { CURL_CBAPI_FN_multi_timer_cb, 1 }, |
129 | | }; |
130 | | |
131 | | static bool eapi_in_event_cb(struct Curl_easy *data) |
132 | 0 | { |
133 | 0 | struct Curl_multi *multi = data->multi; |
134 | 0 | if(multi && multi->callstack.count) { |
135 | 0 | size_t i; |
136 | 0 | for(i = 0; i < multi->callstack.count; ++i) { |
137 | 0 | if(multi->callstack.calls[i] >= CURL_CBAPI_FN_START) { |
138 | 0 | uint16_t fn = multi->callstack.calls[i]; |
139 | 0 | if((fn < CURL_CBAPI_FN_LAST) && |
140 | 0 | cbapi_fn_props[fn - CURL_CBAPI_FN_START].is_event_cb) |
141 | 0 | return TRUE; |
142 | 0 | } |
143 | 0 | } |
144 | 0 | } |
145 | 0 | return FALSE; |
146 | 0 | } |
147 | | |
148 | | static bool mapi_in_ntfy_cb(struct Curl_multi *multi) |
149 | 0 | { |
150 | 0 | if(multi && multi->callstack.count) { |
151 | 0 | size_t i; |
152 | 0 | for(i = 0; i < multi->callstack.count; ++i) { |
153 | 0 | if(multi->callstack.calls[i] == CURL_CBAPI_FN_multi_ntfy_cb) |
154 | 0 | return TRUE; |
155 | 0 | } |
156 | 0 | } |
157 | 0 | return FALSE; |
158 | 0 | } |
159 | | |
160 | | bool Curl_api_multi_is_in_callback(struct Curl_multi *multi) |
161 | 0 | { |
162 | 0 | if(multi && multi->callstack.count) { |
163 | 0 | size_t i; |
164 | 0 | for(i = 0; i < multi->callstack.count; ++i) { |
165 | 0 | if(multi->callstack.calls[i] >= CURL_CBAPI_FN_START) |
166 | 0 | return TRUE; |
167 | 0 | } |
168 | 0 | } |
169 | 0 | return FALSE; |
170 | 0 | } |
171 | | |
172 | | bool Curl_api_is_in_callback(struct Curl_easy *data) |
173 | 0 | { |
174 | 0 | if(data && data->multi) { |
175 | 0 | return Curl_api_multi_is_in_callback(data->multi); |
176 | 0 | } |
177 | 0 | return FALSE; |
178 | 0 | } |
179 | | |
180 | | bool Curl_eapi_enter(struct Curl_eapi_guard *guard, |
181 | | CURL *curl, |
182 | | Curl_eapi_fn fn, |
183 | | CURLcode *presult) |
184 | 0 | { |
185 | 0 | struct Curl_easy *data = curl; |
186 | 0 | const struct Curl_eapi_fn_props *fn_props; |
187 | 0 | CURLcode result = CURLE_OK; |
188 | |
|
189 | 0 | guard->depth = 0; |
190 | | |
191 | | /* Verify that we got an easy handle we can work with. */ |
192 | 0 | if(!GOOD_EASY_HANDLE(data)) { |
193 | 0 | result = CURLE_BAD_FUNCTION_ARGUMENT; |
194 | 0 | goto out; |
195 | 0 | } |
196 | | /* verify that is either not added to a multi handle OR has a |
197 | | * GOOD multi handle that knows `data` for `data->mid`. */ |
198 | 0 | if(data->mid != UINT32_MAX) { |
199 | 0 | if(GOOD_MULTI_HANDLE(data->multi)) { |
200 | 0 | if(!Curl_multi_knows_easy(data->multi, data)) { |
201 | | /* But multi does not know it, something is fishy, better deny call */ |
202 | 0 | DEBUGASSERT(0); |
203 | 0 | result = CURLE_BAD_FUNCTION_ARGUMENT; |
204 | 0 | goto out; |
205 | 0 | } |
206 | 0 | } |
207 | 0 | else { |
208 | 0 | DEBUGASSERT(0); /* data needs to have a GOOD multi handle */ |
209 | 0 | result = CURLE_BAD_FUNCTION_ARGUMENT; |
210 | 0 | goto out; |
211 | 0 | } |
212 | 0 | } |
213 | 0 | else if(data->multi) { |
214 | 0 | DEBUGASSERT(0); /* data should not have a multi handle */ |
215 | 0 | result = CURLE_BAD_FUNCTION_ARGUMENT; |
216 | 0 | goto out; |
217 | 0 | } |
218 | | /* verify that the call `fn` we're about to enter is known |
219 | | * and check call properties to be admitting. */ |
220 | 0 | if(fn >= CURL_EAPI_FN_LAST) { |
221 | 0 | result = CURLE_BAD_FUNCTION_ARGUMENT; |
222 | 0 | goto out; |
223 | 0 | } |
224 | 0 | fn_props = &eapi_fn_props[fn]; |
225 | 0 | DEBUGASSERT(fn_props->fn == fn); |
226 | 0 | if(!fn_props->recurse) { |
227 | 0 | if(data->callstack.count) { |
228 | 0 | #ifdef CURLVERBOSE |
229 | 0 | DEBUGF(curl_mfprintf(stderr, |
230 | 0 | "EAPI guard: calling %hu with call to %u ongoing\n", (uint16_t)fn, |
231 | 0 | data->callstack.calls[data->callstack.count-1])); |
232 | 0 | #endif |
233 | 0 | result = CURLE_RECURSIVE_API_CALL; |
234 | 0 | goto out; |
235 | 0 | } |
236 | 0 | if(data->multi && data->multi->callstack.count) { |
237 | 0 | #ifdef CURLVERBOSE |
238 | |
|
239 | 0 | DEBUGF(curl_mfprintf(stderr, |
240 | 0 | "EAPI guard: calling %hu with multi call to %u ongoing\n", |
241 | 0 | (uint16_t)fn, |
242 | 0 | data->multi->callstack.calls[data->multi->callstack.count-1])); |
243 | 0 | #endif |
244 | 0 | result = CURLE_RECURSIVE_API_CALL; |
245 | 0 | goto out; |
246 | 0 | } |
247 | 0 | } |
248 | | |
249 | 0 | if(fn_props->no_event_cb && eapi_in_event_cb(data)) { |
250 | | /* Not allowed to be invoked while an event cb is ongoing */ |
251 | 0 | #ifdef CURLVERBOSE |
252 | 0 | DEBUGF(curl_mfprintf(stderr, |
253 | 0 | "EAPI guard: calling %hu while event callback ongoing\n", |
254 | 0 | (uint16_t)fn)); |
255 | 0 | #endif |
256 | 0 | result = CURLE_RECURSIVE_API_CALL; |
257 | 0 | goto out; |
258 | 0 | } |
259 | | |
260 | 0 | #if defined(USE_SSL) && defined(USE_MUTEX) |
261 | 0 | if(fn_props->no_scache_lock && |
262 | 0 | Curl_ssl_scache_is_locked_by_current_thread(data)) { |
263 | 0 | #ifdef CURLVERBOSE |
264 | 0 | DEBUGF(curl_mfprintf(stderr, |
265 | 0 | "EAPI guard: calling %hu while vtls_scache is locked by " |
266 | 0 | "current thread\n", (uint16_t)fn)); |
267 | 0 | #endif |
268 | 0 | result = CURLE_RECURSIVE_API_CALL; |
269 | 0 | goto out; |
270 | 0 | } |
271 | 0 | #endif |
272 | | |
273 | | /* all fine, add to data's callstack */ |
274 | 0 | if(data->callstack.count >= CURL_EAPI_MAX_RECURSION) { |
275 | 0 | result = CURLE_RECURSIVE_API_CALL; |
276 | 0 | goto out; |
277 | 0 | } |
278 | 0 | data->callstack.calls[data->callstack.count] = (uint16_t)fn; |
279 | 0 | ++data->callstack.count; |
280 | 0 | guard->depth = data->callstack.count; |
281 | 0 | guard->data = fn_props->data_is_killed ? NULL : data; |
282 | |
|
283 | 0 | out: |
284 | 0 | if(presult) |
285 | 0 | *presult = result; |
286 | 0 | return guard->depth > 0; |
287 | 0 | } |
288 | | |
289 | | void Curl_eapi_leave(struct Curl_eapi_guard *guard) |
290 | 0 | { |
291 | 0 | if(guard->depth) { |
292 | | /* guard->data is set when handle is supposed to stay alive during call */ |
293 | 0 | if(guard->data && GOOD_EASY_HANDLE(guard->data)) { |
294 | 0 | if(guard->depth > guard->data->callstack.count) { |
295 | 0 | DEBUGASSERT(0); /* something very wrong */ |
296 | 0 | } |
297 | 0 | else { |
298 | 0 | if(guard->depth < guard->data->callstack.count) { |
299 | 0 | DEBUGASSERT(0); /* someone forgot to clean up */ |
300 | 0 | } |
301 | | /* reset to depth the guard was entered in */ |
302 | 0 | guard->data->callstack.count = (uint16_t)(guard->depth - 1); |
303 | 0 | } |
304 | 0 | } |
305 | 0 | } |
306 | 0 | } |
307 | | |
308 | | CURLHcode Curl_eapi_hcode(CURLcode result) |
309 | 0 | { |
310 | 0 | switch(result) { |
311 | 0 | case CURLE_OK: |
312 | 0 | return CURLHE_OK; |
313 | 0 | case CURLE_BAD_FUNCTION_ARGUMENT: |
314 | 0 | return CURLHE_BAD_ARGUMENT; |
315 | 0 | case CURLE_OUT_OF_MEMORY: |
316 | 0 | return CURLHE_OUT_OF_MEMORY; |
317 | 0 | case CURLE_NOT_BUILT_IN: |
318 | 0 | return CURLHE_NOT_BUILT_IN; |
319 | 0 | default: |
320 | | /* Unfortunately, we cannot convert RECURSIVE_API_CALL, |
321 | | * but since the header API is reentrant, this should not happen. */ |
322 | 0 | return CURLHE_BAD_ARGUMENT; |
323 | 0 | } |
324 | 0 | } |
325 | | |
326 | | bool Curl_mapi_enter(struct Curl_mapi_guard *guard, |
327 | | CURLM *m, |
328 | | Curl_mapi_fn fn, |
329 | | CURLMcode *pmresult) |
330 | 0 | { |
331 | 0 | struct Curl_multi *multi = m; |
332 | 0 | const struct Curl_mapi_fn_props *fn_props; |
333 | 0 | CURLMcode mresult = CURLM_OK; |
334 | |
|
335 | 0 | guard->depth = 0; |
336 | | |
337 | | /* Verify that we got an easy handle we can work with. */ |
338 | 0 | if(!GOOD_MULTI_HANDLE(multi)) { |
339 | 0 | mresult = CURLM_BAD_HANDLE; |
340 | 0 | goto out; |
341 | 0 | } |
342 | 0 | if(fn >= CURL_MAPI_FN_LAST) { |
343 | 0 | mresult = CURLM_BAD_FUNCTION_ARGUMENT; |
344 | 0 | goto out; |
345 | 0 | } |
346 | 0 | fn_props = &mapi_fn_props[fn]; |
347 | 0 | DEBUGASSERT(fn_props->fn == fn); |
348 | 0 | if(fn_props->allow_ntfy_cb && mapi_in_ntfy_cb(multi)) { |
349 | | /* explicitly allowed, even though normal recursion may not */ |
350 | 0 | } |
351 | 0 | else if(!fn_props->recurse && multi->callstack.count) { |
352 | 0 | #ifdef CURLVERBOSE |
353 | 0 | DEBUGF(curl_mfprintf(stderr, |
354 | 0 | "MAPI guard: calling %hu with call to %u ongoing\n", (uint16_t)fn, |
355 | 0 | multi->callstack.calls[multi->callstack.count-1])); |
356 | 0 | #endif |
357 | 0 | mresult = CURLM_RECURSIVE_API_CALL; |
358 | 0 | goto out; |
359 | 0 | } |
360 | | |
361 | 0 | #if defined(USE_SSL) && defined(USE_MUTEX) |
362 | 0 | if(fn_props->no_scache_lock && multi->ssl_scache && |
363 | 0 | Curl_ssl_scache_is_locked_by_current_thread(multi->admin)) { |
364 | 0 | #ifdef CURLVERBOSE |
365 | 0 | DEBUGF(curl_mfprintf(stderr, |
366 | 0 | "MAPI guard: calling %hu while its vtls_scache is locked by " |
367 | 0 | "current thread\n", (uint16_t)fn)); |
368 | 0 | #endif |
369 | 0 | mresult = CURLM_RECURSIVE_API_CALL; |
370 | 0 | goto out; |
371 | 0 | } |
372 | 0 | #endif |
373 | | |
374 | | /* all fine, add to data's callstack */ |
375 | 0 | if(multi->callstack.count >= CURL_MAPI_MAX_RECURSION) { |
376 | 0 | mresult = CURLM_RECURSIVE_API_CALL; |
377 | 0 | goto out; |
378 | 0 | } |
379 | 0 | multi->callstack.calls[multi->callstack.count] = (uint16_t)fn; |
380 | 0 | ++multi->callstack.count; |
381 | 0 | guard->depth = multi->callstack.count; |
382 | 0 | guard->multi = fn_props->multi_is_killed ? NULL : multi; |
383 | |
|
384 | 0 | out: |
385 | 0 | if(pmresult) |
386 | 0 | *pmresult = mresult; |
387 | 0 | return guard->depth > 0; |
388 | 0 | } |
389 | | |
390 | | void Curl_mapi_leave(struct Curl_mapi_guard *guard) |
391 | 0 | { |
392 | 0 | if(guard->depth) { |
393 | | /* guard->data is set when handle is supposed to stay alive during call */ |
394 | 0 | if(guard->multi && GOOD_MULTI_HANDLE(guard->multi)) { |
395 | 0 | if(guard->depth > guard->multi->callstack.count) { |
396 | 0 | DEBUGASSERT(0); /* something very wrong */ |
397 | 0 | } |
398 | 0 | else { |
399 | 0 | if(guard->depth < guard->multi->callstack.count) { |
400 | 0 | DEBUGASSERT(0); /* someone forgot to clean up */ |
401 | 0 | } |
402 | | /* reset to depth the guard was entered in */ |
403 | 0 | guard->multi->callstack.count = (uint16_t)(guard->depth - 1); |
404 | 0 | } |
405 | 0 | } |
406 | 0 | } |
407 | 0 | } |
408 | | |
409 | | void Curl_cbapi_enter(struct Curl_mapi_guard *guard, |
410 | | struct Curl_easy *data, |
411 | | struct Curl_multi *multi, |
412 | | Curl_cbapi_fn fn) |
413 | 0 | { |
414 | 0 | guard->depth = 0; |
415 | |
|
416 | 0 | if(!multi) |
417 | 0 | multi = data ? data->multi : NULL; |
418 | | /* if not multi is involved here, just leave */ |
419 | 0 | if(!multi) |
420 | 0 | return; |
421 | | /* invalid callback specifier? */ |
422 | 0 | if((fn >= CURL_CBAPI_FN_LAST) || (fn < CURL_CBAPI_FN_START)) { |
423 | 0 | DEBUGASSERT(0); |
424 | 0 | return; |
425 | 0 | } |
426 | 0 | DEBUGASSERT(cbapi_fn_props[fn - CURL_CBAPI_FN_START].fn == fn); |
427 | 0 | if(multi->callstack.count) { |
428 | 0 | size_t i; |
429 | 0 | for(i = multi->callstack.count; i; --i) { |
430 | 0 | if(multi->callstack.calls[i - 1] == fn) { |
431 | | /* recursive invocation of the same callback */ |
432 | 0 | DEBUGASSERT(0); |
433 | 0 | return; |
434 | 0 | } |
435 | 0 | } |
436 | 0 | } |
437 | | |
438 | | /* all fine, add to data's callstack */ |
439 | | /* if multi callstack already at max depth, leave */ |
440 | 0 | if(multi->callstack.count >= CURL_MAPI_MAX_RECURSION) |
441 | 0 | return; |
442 | 0 | multi->callstack.calls[multi->callstack.count] = (uint16_t)fn; |
443 | 0 | ++multi->callstack.count; |
444 | 0 | guard->depth = multi->callstack.count; |
445 | 0 | guard->multi = multi; |
446 | 0 | } |
447 | | |
448 | | void Curl_cbapi_leave(struct Curl_mapi_guard *guard) |
449 | 0 | { |
450 | 0 | Curl_mapi_leave(guard); |
451 | 0 | } |