Skip to content
This repository was archived by the owner on Jan 2, 2025. It is now read-only.

Commit 6bd70e2

Browse files
authored
Add wrapper for select() returning Err (#41)
* Add select wrapper returning a Err * Add tests * Remember to zero errno * Attempt to fix windows build * Attempt to fix Windows 32 bit build
1 parent ac2efc1 commit 6bd70e2

File tree

3 files changed

+167
-90
lines changed

3 files changed

+167
-90
lines changed

libndt.cpp

Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -437,23 +437,22 @@ bool Client::wait_close() noexcept {
437437
FD_ZERO(&readset);
438438
// In wait_close() regress test we call wait_close() with sock
439439
// equal to -1, which causes a segfault. For testability do not
440-
// reject the value but rather just ignore the socket.
440+
// reject the value but rather just ignore the socket. We have
441+
// an assert() below that catches other negative values.
441442
if (impl->sock >= 0) {
442443
FD_SET(AS_OS_SOCKET(impl->sock), &readset);
443444
}
444445
timeval tv{};
445446
tv.tv_sec = 1;
446447
// Note: cast to `int` safe because on Unix sockets are `int`s and on
447448
// Windows instead the first argment to select() is ignored.
448-
auto rv = this->select((int)impl->sock + 1, &readset, nullptr, nullptr, &tv);
449-
if (rv < 0 && !OS_ERROR_IS_EINTR()) {
450-
EMIT_WARNING("wait_close(): select() failed: " << get_last_system_error());
451-
return false;
452-
}
453-
if (rv <= 0) {
454-
EMIT_DEBUG("wait_close(): timeout or EINTR waiting for EOF on connection");
449+
assert(impl->sock >= -1 && impl->sock < INT_MAX);
450+
auto err = netx_select((int)impl->sock + 1, &readset, nullptr, nullptr, &tv);
451+
if (err != Err::none) {
452+
EMIT_WARNING(
453+
"wait_close(): netx_select() failed: " << get_last_system_error());
455454
(void)this->shutdown(impl->sock, OS_SHUT_RDWR);
456-
return true; // be tolerant
455+
return (err == Err::timed_out);
457456
}
458457
{
459458
char data;
@@ -517,13 +516,14 @@ bool Client::run_download() noexcept {
517516
timeval tv{};
518517
tv.tv_usec = 250000;
519518
// Cast to `int` safe as explained above.
520-
auto rv = this->select((int)maxsock + 1, &set, nullptr, nullptr, &tv);
521-
if (rv < 0 && !OS_ERROR_IS_EINTR()) {
519+
assert(maxsock < INT_MAX);
520+
auto err = netx_select((int)maxsock + 1, &set, nullptr, nullptr, &tv);
521+
if (err != Err::none && err != Err::timed_out) {
522522
EMIT_WARNING(
523-
"run_download: select() failed: " << get_last_system_error());
523+
"run_download: netx_select() failed: " << get_last_system_error());
524524
return false;
525525
}
526-
if (rv > 0) {
526+
if (err == Err::none) {
527527
for (auto &fd : dload_socks.sockets) {
528528
if (FD_ISSET(fd, &set)) {
529529
Size n = 0;
@@ -682,13 +682,14 @@ bool Client::run_upload() noexcept {
682682
timeval tv{};
683683
tv.tv_usec = 250000;
684684
// Cast to `int` safe as explained above.
685-
auto rv = this->select((int)maxsock + 1, nullptr, &set, nullptr, &tv);
686-
if (rv < 0 && !OS_ERROR_IS_EINTR()) {
685+
assert(maxsock < INT_MAX);
686+
auto err = netx_select((int)maxsock + 1, nullptr, &set, nullptr, &tv);
687+
if (err != Err::none && err != Err::timed_out) {
687688
EMIT_WARNING(
688-
"run_upload: select() failed: " << get_last_system_error());
689+
"run_upload: netx_select() failed: " << get_last_system_error());
689690
return false;
690691
}
691-
if (rv > 0) {
692+
if (err == Err::none) {
692693
for (auto &fd : upload_socks.sockets) {
693694
if (FD_ISSET(fd, &set)) {
694695
Size n = 0;
@@ -1351,7 +1352,7 @@ Err Client::netx_recv(Socket fd, void *base, Size count,
13511352
Size *actual) noexcept {
13521353
if (count <= 0) {
13531354
EMIT_WARNING(
1354-
"netx_recv: explicitly disallowing zero read; use select() "
1355+
"netx_recv: explicitly disallowing zero read; use netx_select() "
13551356
"to check the state of a socket");
13561357
return Err::invalid_argument;
13571358
}
@@ -1388,7 +1389,7 @@ Err Client::netx_send(Socket fd, const void *base, Size count,
13881389
Size *actual) noexcept {
13891390
if (count <= 0) {
13901391
EMIT_WARNING(
1391-
"netx_send: explicitly disallowing zero send; use select() "
1392+
"netx_send: explicitly disallowing zero send; use netx_select() "
13921393
"to check the state of a socket");
13931394
return Err::invalid_argument;
13941395
}
@@ -1490,6 +1491,27 @@ Err Client::netx_setnonblocking(Socket fd, bool enable) noexcept {
14901491
return Err::none;
14911492
}
14921493

1494+
Err Client::netx_select(int numfd, fd_set *readset, fd_set *writeset,
1495+
fd_set *exceptset, timeval *tvp) noexcept {
1496+
auto rv = 0;
1497+
auto err = Err::none;
1498+
again:
1499+
set_last_system_error(0);
1500+
rv = this->select(numfd, readset, writeset, exceptset, tvp);
1501+
if (rv < 0) {
1502+
assert(rv == -1);
1503+
err = netx_map_errno(get_last_system_error());
1504+
if (err == Err::interrupted) {
1505+
goto again;
1506+
}
1507+
return err;
1508+
}
1509+
if (rv == 0) {
1510+
return Err::timed_out;
1511+
}
1512+
return Err::none;
1513+
}
1514+
14931515
// Dependencies (curl)
14941516

14951517
uint64_t Client::get_verbosity() const noexcept {

libndt.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,9 @@ class Client {
341341

342342
virtual Err netx_setnonblocking(Socket fd, bool enable) noexcept;
343343

344+
virtual Err netx_select(int numfd, fd_set *readset, fd_set *writeset,
345+
fd_set *exceptset, timeval *timeout) noexcept;
346+
344347
// Dependencies (cURL)
345348

346349
uint64_t get_verbosity() const noexcept;

0 commit comments

Comments
 (0)