@@ -437,23 +437,22 @@ bool Client::wait_close() noexcept {
437
437
FD_ZERO (&readset);
438
438
// In wait_close() regress test we call wait_close() with sock
439
439
// 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.
441
442
if (impl->sock >= 0 ) {
442
443
FD_SET (AS_OS_SOCKET (impl->sock ), &readset);
443
444
}
444
445
timeval tv{};
445
446
tv.tv_sec = 1 ;
446
447
// Note: cast to `int` safe because on Unix sockets are `int`s and on
447
448
// 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 ());
455
454
(void )this ->shutdown (impl->sock , OS_SHUT_RDWR);
456
- return true ; // be tolerant
455
+ return (err == Err::timed_out);
457
456
}
458
457
{
459
458
char data;
@@ -517,13 +516,14 @@ bool Client::run_download() noexcept {
517
516
timeval tv{};
518
517
tv.tv_usec = 250000 ;
519
518
// 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) {
522
522
EMIT_WARNING (
523
- " run_download: select () failed: " << get_last_system_error ());
523
+ " run_download: netx_select () failed: " << get_last_system_error ());
524
524
return false ;
525
525
}
526
- if (rv > 0 ) {
526
+ if (err == Err::none ) {
527
527
for (auto &fd : dload_socks.sockets ) {
528
528
if (FD_ISSET (fd, &set)) {
529
529
Size n = 0 ;
@@ -682,13 +682,14 @@ bool Client::run_upload() noexcept {
682
682
timeval tv{};
683
683
tv.tv_usec = 250000 ;
684
684
// 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) {
687
688
EMIT_WARNING (
688
- " run_upload: select () failed: " << get_last_system_error ());
689
+ " run_upload: netx_select () failed: " << get_last_system_error ());
689
690
return false ;
690
691
}
691
- if (rv > 0 ) {
692
+ if (err == Err::none ) {
692
693
for (auto &fd : upload_socks.sockets ) {
693
694
if (FD_ISSET (fd, &set)) {
694
695
Size n = 0 ;
@@ -1351,7 +1352,7 @@ Err Client::netx_recv(Socket fd, void *base, Size count,
1351
1352
Size *actual) noexcept {
1352
1353
if (count <= 0 ) {
1353
1354
EMIT_WARNING (
1354
- " netx_recv: explicitly disallowing zero read; use select () "
1355
+ " netx_recv: explicitly disallowing zero read; use netx_select () "
1355
1356
" to check the state of a socket" );
1356
1357
return Err::invalid_argument;
1357
1358
}
@@ -1388,7 +1389,7 @@ Err Client::netx_send(Socket fd, const void *base, Size count,
1388
1389
Size *actual) noexcept {
1389
1390
if (count <= 0 ) {
1390
1391
EMIT_WARNING (
1391
- " netx_send: explicitly disallowing zero send; use select () "
1392
+ " netx_send: explicitly disallowing zero send; use netx_select () "
1392
1393
" to check the state of a socket" );
1393
1394
return Err::invalid_argument;
1394
1395
}
@@ -1490,6 +1491,27 @@ Err Client::netx_setnonblocking(Socket fd, bool enable) noexcept {
1490
1491
return Err::none;
1491
1492
}
1492
1493
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
+
1493
1515
// Dependencies (curl)
1494
1516
1495
1517
uint64_t Client::get_verbosity () const noexcept {
0 commit comments