]> git.ipfire.org Git - thirdparty/psycopg.git/commitdiff
fix(c): fix timeout calculation in select-based wait function
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>
Fri, 10 Oct 2025 13:17:54 +0000 (15:17 +0200)
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>
Mon, 13 Oct 2025 11:26:01 +0000 (13:26 +0200)
Because of the error, the timeout set to 0, resulting in a busy loop
instead of waiting.

Might fix #645.

docs/news.rst
psycopg_c/psycopg_c/_psycopg/waiting.pyx
tests/test_waiting.py
tests/test_waiting_async.py

index 7c3d6929eca217c2d318b175ca4eb4f2987fa853..0156e21c2cb998a9051ebfa91d8f683aae863936 100644 (file)
@@ -14,6 +14,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`).
index 3c22a6aca598d376da815270d5e281ca39f79cc9..bd60399714091abd81f77360c661c8ac5593c960 100644 (file)
@@ -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;
     }
 
index a3fa0f710216c404e7990972b365cbbce8aa03a5..c8c03b88202f3076bb39e8f9414cdd276db42822 100644 (file)
@@ -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"
index e274fc1f52444039d4ce7ebe061f2a8c24c07ce8..dc3cf8cc1af29e7c37acf5aa8f3907bb0cbe5eb5 100644 (file)
@@ -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"