| 247 | } |
| 248 | |
| 249 | void Sock::SendComplete(std::span<const unsigned char> data, |
| 250 | std::chrono::milliseconds timeout, |
| 251 | CThreadInterrupt& interrupt) const |
| 252 | { |
| 253 | const auto deadline = GetTime<std::chrono::milliseconds>() + timeout; |
| 254 | size_t sent{0}; |
| 255 | |
| 256 | for (;;) { |
| 257 | const ssize_t ret{Send(data.data() + sent, data.size() - sent, MSG_NOSIGNAL)}; |
| 258 | |
| 259 | if (ret > 0) { |
| 260 | sent += static_cast<size_t>(ret); |
| 261 | if (sent == data.size()) { |
| 262 | break; |
| 263 | } |
| 264 | } else { |
| 265 | const int err{WSAGetLastError()}; |
| 266 | if (IOErrorIsPermanent(err)) { |
| 267 | throw std::runtime_error(strprintf("send(): %s", NetworkErrorString(err))); |
| 268 | } |
| 269 | } |
| 270 | |
| 271 | const auto now = GetTime<std::chrono::milliseconds>(); |
| 272 | |
| 273 | if (now >= deadline) { |
| 274 | throw std::runtime_error(strprintf( |
| 275 | "Send timeout (sent only %u of %u bytes before that)", sent, data.size())); |
| 276 | } |
| 277 | |
| 278 | if (interrupt) { |
| 279 | throw std::runtime_error(strprintf( |
| 280 | "Send interrupted (sent only %u of %u bytes before that)", sent, data.size())); |
| 281 | } |
| 282 | |
| 283 | // Wait for a short while (or the socket to become ready for sending) before retrying |
| 284 | // if nothing was sent. |
| 285 | const auto wait_time = std::min(deadline - now, std::chrono::milliseconds{MAX_WAIT_FOR_IO}); |
| 286 | (void)Wait(wait_time, SendEvent); |
| 287 | } |
| 288 | } |
| 289 | |
| 290 | void Sock::SendComplete(std::span<const char> data, |
| 291 | std::chrono::milliseconds timeout, |