| 402 | #define NEXT_ATTEMPT_DELAY_MS 200 |
| 403 | |
| 404 | int ff_connect_parallel(struct addrinfo *addrs, int timeout_ms_per_address, |
| 405 | int parallel, URLContext *h, int *fd, |
| 406 | int (*customize_fd)(void *, int, int), void *customize_ctx) |
| 407 | { |
| 408 | struct ConnectionAttempt attempts[3]; |
| 409 | struct pollfd pfd[3]; |
| 410 | int nb_attempts = 0, i, j; |
| 411 | int64_t next_attempt_us = av_gettime_relative(), next_deadline_us; |
| 412 | int last_err = AVERROR(EIO); |
| 413 | socklen_t optlen; |
| 414 | char hostbuf[100], portbuf[20]; |
| 415 | |
| 416 | if (parallel > FF_ARRAY_ELEMS(attempts)) |
| 417 | parallel = FF_ARRAY_ELEMS(attempts); |
| 418 | |
| 419 | print_address_list(h, addrs, "Original list of addresses"); |
| 420 | // This mutates the list, but the head of the list is still the same |
| 421 | // element, so the caller, who owns the list, doesn't need to get |
| 422 | // an updated pointer. |
| 423 | interleave_addrinfo(addrs); |
| 424 | print_address_list(h, addrs, "Interleaved list of addresses"); |
| 425 | |
| 426 | while (nb_attempts > 0 || addrs) { |
| 427 | // Start a new connection attempt, if possible. |
| 428 | if (nb_attempts < parallel && addrs) { |
| 429 | getnameinfo(addrs->ai_addr, addrs->ai_addrlen, |
| 430 | hostbuf, sizeof(hostbuf), portbuf, sizeof(portbuf), |
| 431 | NI_NUMERICHOST | NI_NUMERICSERV); |
| 432 | av_log(h, AV_LOG_VERBOSE, "Starting connection attempt to %s port %s\n", |
| 433 | hostbuf, portbuf); |
| 434 | last_err = start_connect_attempt(&attempts[nb_attempts], &addrs, |
| 435 | timeout_ms_per_address, h, |
| 436 | customize_fd, customize_ctx); |
| 437 | if (last_err < 0) { |
| 438 | av_log(h, AV_LOG_VERBOSE, "Connected attempt failed: %s\n", |
| 439 | av_err2str(last_err)); |
| 440 | continue; |
| 441 | } |
| 442 | if (last_err > 0) { |
| 443 | for (i = 0; i < nb_attempts; i++) |
| 444 | closesocket(attempts[i].fd); |
| 445 | *fd = attempts[nb_attempts].fd; |
| 446 | return 0; |
| 447 | } |
| 448 | pfd[nb_attempts].fd = attempts[nb_attempts].fd; |
| 449 | pfd[nb_attempts].events = POLLOUT; |
| 450 | next_attempt_us = av_gettime_relative() + NEXT_ATTEMPT_DELAY_MS * 1000; |
| 451 | nb_attempts++; |
| 452 | } |
| 453 | |
| 454 | av_assert0(nb_attempts > 0); |
| 455 | // The connection attempts are sorted from oldest to newest, so the |
| 456 | // first one will have the earliest deadline. |
| 457 | next_deadline_us = attempts[0].deadline_us; |
| 458 | // If we can start another attempt in parallel, wait until that time. |
| 459 | if (nb_attempts < parallel && addrs) |
| 460 | next_deadline_us = FFMIN(next_deadline_us, next_attempt_us); |
| 461 | last_err = ff_poll_interrupt(pfd, nb_attempts, |
no test coverage detected