From: Daniele Varrazzo Date: Fri, 10 Oct 2025 13:17:54 +0000 (+0200) Subject: fix(c): fix timeout calculation in select-based wait function X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=46d2721192b0f401aaa5280e2834bc4bb8116204;p=thirdparty%2Fpsycopg.git fix(c): fix timeout calculation in select-based wait function Because of the error, the timeout set to 0, resulting in a busy loop instead of waiting. Might fix #645. --- diff --git a/docs/news.rst b/docs/news.rst index 7487d2d34..46a163fa7 100644 --- a/docs/news.rst +++ b/docs/news.rst @@ -47,6 +47,7 @@ Psycopg 3.2.11 (unreleased) ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Fix spurious readiness flags in some of the wait functions (:ticket:`#1141`). +- Fix high CPU usage using the ``wait_c`` function on Windows (:ticket:`#645`). - Fix bad data on error in binary copy (:ticket:`#1147`). - Don't raise warning, and don't leak resources, if a builtin function is used as JSON dumper/loader function (:ticket:`#1165`). diff --git a/psycopg_c/psycopg_c/_psycopg/waiting.pyx b/psycopg_c/psycopg_c/_psycopg/waiting.pyx index 3c22a6aca..bd6039971 100644 --- a/psycopg_c/psycopg_c/_psycopg/waiting.pyx +++ b/psycopg_c/psycopg_c/_psycopg/waiting.pyx @@ -134,7 +134,7 @@ retry_eintr: } else { tv.tv_sec = (int)timeout; - tv.tv_usec = (int)(((long)timeout * SEC_TO_US) % SEC_TO_US); + tv.tv_usec = (int)((long)(timeout * SEC_TO_US) % SEC_TO_US); tvptr = &tv; } diff --git a/tests/test_waiting.py b/tests/test_waiting.py index a3fa0f710..c8c03b882 100644 --- a/tests/test_waiting.py +++ b/tests/test_waiting.py @@ -287,6 +287,30 @@ def test_wait_timeout(pgconn, waitfn): assert d == pytest.approx(0.1, 0.05) +@pytest.mark.slow +@pytest.mark.parametrize("waitfns", waitfns) +def test_wait_no_busy_loop(pgconn, waitfns): + waitfn = getattr(waiting, waitfns) + + pgconn.send_query(b"select pg_sleep(1)") + gen = generators.execute(pgconn) + ncalls = 0 + + def gen_wrapper(): + nonlocal ncalls + try: + for x in gen: + res = yield x + ncalls += 1 + gen.send(res) + except StopIteration as ex: + return ex.value + + (res,) = waitfn(gen_wrapper(), pgconn.socket, 0.3) + assert res.status == ExecStatus.TUPLES_OK + assert ncalls < 5 + + @pytest.mark.slow @pytest.mark.skipif( "sys.platform == 'win32'", reason="win32 works ok, but FDs are mysterious" diff --git a/tests/test_waiting_async.py b/tests/test_waiting_async.py index e274fc1f5..dc3cf8cc1 100644 --- a/tests/test_waiting_async.py +++ b/tests/test_waiting_async.py @@ -295,6 +295,30 @@ async def test_wait_timeout(pgconn, waitfn): assert d == pytest.approx(0.1, 0.05) +@pytest.mark.slow +@pytest.mark.parametrize("waitfns", waitfns) +async def test_wait_no_busy_loop(pgconn, waitfns): + waitfn = getattr(waiting, waitfns) + + pgconn.send_query(b"select pg_sleep(1)") + gen = generators.execute(pgconn) + ncalls = 0 + + def gen_wrapper(): + nonlocal ncalls + try: + for x in gen: + res = yield x + ncalls += 1 + gen.send(res) + except StopIteration as ex: + return ex.value + + (res,) = await waitfn(gen_wrapper(), pgconn.socket, 0.3) + assert res.status == ExecStatus.TUPLES_OK + assert ncalls < 5 + + @pytest.mark.slow @pytest.mark.skipif( "sys.platform == 'win32'", reason="win32 works ok, but FDs are mysterious"