Skip to content

Commit ff5b082

Browse files
jchampiomacdice
andcommitted
oauth: Remove stale events from the kqueue multiplexer
If a socket is added to the kqueue, becomes readable/writable, and subsequently becomes non-readable/writable again, the kqueue itself will remain readable until either the socket registration is removed, or the stale event is cleared via a call to kevent(). In many simple cases, Curl itself will remove the socket registration quickly, but in real-world usage, this is not guaranteed to happen. The kqueue can then remain stuck in a permanently readable state until the request ends, which results in pointless wakeups for the client and wasted CPU time. Implement comb_multiplexer() to call kevent() and unstick any stale events that would cause unnecessary callbacks. This is called right after drive_request(), before we return control to the client to wait. Suggested-by: Thomas Munro <thomas.munro@gmail.com> Co-authored-by: Thomas Munro <thomas.munro@gmail.com> Reviewed-by: Thomas Munro <thomas.munro@gmail.com> Backpatch-through: 18 Discussion: https://postgr.es/m/CAOYmi+nDZxJHaWj9_jRSyf8uMToCADAmOfJEggsKW-kY7aUwHA@mail.gmail.com
1 parent b5cd746 commit ff5b082

File tree

1 file changed

+61
-6
lines changed

1 file changed

+61
-6
lines changed

src/interfaces/libpq-oauth/oauth-curl.c

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1376,6 +1376,53 @@ register_socket(CURL *curl, curl_socket_t socket, int what, void *ctx,
13761376
#endif
13771377
}
13781378

1379+
/*
1380+
* If there is no work to do on any of the descriptors in the multiplexer, then
1381+
* this function must ensure that the multiplexer is not readable.
1382+
*
1383+
* Unlike epoll descriptors, kqueue descriptors only transition from readable to
1384+
* unreadable when kevent() is called and finds nothing, after removing
1385+
* level-triggered conditions that have gone away. We therefore need a dummy
1386+
* kevent() call after operations might have been performed on the monitored
1387+
* sockets or timer_fd. Any event returned is ignored here, but it also remains
1388+
* queued (being level-triggered) and leaves the descriptor readable. This is a
1389+
* no-op for epoll descriptors.
1390+
*/
1391+
static bool
1392+
comb_multiplexer(struct async_ctx *actx)
1393+
{
1394+
#if defined(HAVE_SYS_EPOLL_H)
1395+
/* The epoll implementation doesn't hold onto stale events. */
1396+
return true;
1397+
#elif defined(HAVE_SYS_EVENT_H)
1398+
struct timespec timeout = {0};
1399+
struct kevent ev;
1400+
1401+
/*
1402+
* Try to read a single pending event. We can actually ignore the result:
1403+
* either we found an event to process, in which case the multiplexer is
1404+
* correctly readable for that event at minimum, and it doesn't matter if
1405+
* there are any stale events; or we didn't find any, in which case the
1406+
* kernel will have discarded any stale events as it traveled to the end
1407+
* of the queue.
1408+
*
1409+
* Note that this depends on our registrations being level-triggered --
1410+
* even the timer, so we use a chained kqueue for that instead of an
1411+
* EVFILT_TIMER on the top-level mux. If we used edge-triggered events,
1412+
* this call would improperly discard them.
1413+
*/
1414+
if (kevent(actx->mux, NULL, 0, &ev, 1, &timeout) < 0)
1415+
{
1416+
actx_error(actx, "could not comb kqueue: %m");
1417+
return false;
1418+
}
1419+
1420+
return true;
1421+
#else
1422+
#error comb_multiplexer is not implemented on this platform
1423+
#endif
1424+
}
1425+
13791426
/*
13801427
* Enables or disables the timer in the multiplexer set. The timeout value is
13811428
* in milliseconds (negative values disable the timer).
@@ -2755,13 +2802,21 @@ pg_fe_run_oauth_flow_impl(PGconn *conn)
27552802

27562803
if (status == PGRES_POLLING_FAILED)
27572804
goto error_return;
2758-
else if (status != PGRES_POLLING_OK)
2759-
{
2760-
/* not done yet */
2761-
return status;
2762-
}
2805+
else if (status == PGRES_POLLING_OK)
2806+
break; /* done! */
27632807

2764-
break;
2808+
/*
2809+
* This request is still running.
2810+
*
2811+
* Make sure that stale events don't cause us to come back
2812+
* early. (Currently, this can occur only with kqueue.) If
2813+
* this is forgotten, the multiplexer can get stuck in a
2814+
* signaled state and we'll burn CPU cycles pointlessly.
2815+
*/
2816+
if (!comb_multiplexer(actx))
2817+
goto error_return;
2818+
2819+
return status;
27652820
}
27662821

27672822
case OAUTH_STEP_WAIT_INTERVAL:

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy