| 472 | } |
| 473 | |
| 474 | Status SockSendWithTimeout(int fd, const std::string &data, int timeout_ms) { |
| 475 | // Fall back to blocking send if timeout is non-positive |
| 476 | if (timeout_ms <= 0) { |
| 477 | return SockSend(fd, data); |
| 478 | } |
| 479 | |
| 480 | ssize_t n = 0; |
| 481 | auto start = std::chrono::steady_clock::now(); |
| 482 | |
| 483 | while (n < static_cast<ssize_t>(data.size())) { |
| 484 | // Check if we've exceeded the timeout |
| 485 | auto elapsed = |
| 486 | std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start).count(); |
| 487 | if (elapsed >= timeout_ms) { |
| 488 | return {Status::NotOK, fmt::format("send timeout after {} ms, sent {} of {} bytes", elapsed, n, data.size())}; |
| 489 | } |
| 490 | |
| 491 | // Calculate remaining timeout |
| 492 | int remaining_ms = timeout_ms - static_cast<int>(elapsed); |
| 493 | |
| 494 | // Wait for socket to be writable with timeout |
| 495 | int ready = AeWait(fd, AE_WRITABLE, remaining_ms); |
| 496 | if (ready == 0) { |
| 497 | return {Status::NotOK, fmt::format("send timeout waiting for socket, sent {} of {} bytes", n, data.size())}; |
| 498 | } |
| 499 | if (ready < 0) { |
| 500 | return Status::FromErrno("poll error while sending"); |
| 501 | } |
| 502 | |
| 503 | ssize_t nwritten = write(fd, data.data() + n, data.size() - n); |
| 504 | if (nwritten == -1) { |
| 505 | if (errno == EAGAIN || errno == EWOULDBLOCK) { |
| 506 | // Socket buffer is full, continue waiting |
| 507 | continue; |
| 508 | } |
| 509 | return Status::FromErrno(); |
| 510 | } |
| 511 | n += nwritten; |
| 512 | } |
| 513 | return Status::OK(); |
| 514 | } |
| 515 | |
| 516 | Status SockSendWithTimeout(int fd, const std::string &data, [[maybe_unused]] bufferevent *bev, int timeout_ms) { |
| 517 | // Fall back to blocking send if timeout is non-positive |
no test coverage detected