gnunet

Main GNUnet Logic
Log | Files | Refs | Submodules | README | LICENSE

commit bc381df64bab015ea1bfc91315b04656a41ea69d
parent cd1e543ce198c60b058758f93767dd8108c16410
Author: Florian Dold <dold@taler.net>
Date:   Tue,  1 Sep 2026 19:51:32 +0200

pq: poll queued events after synchronous queries

Diffstat:
Msrc/lib/pq/pq.c | 1+
Msrc/lib/pq/pq.h | 10++++++++++
Msrc/lib/pq/pq_event.c | 19+++++++++++++------
Msrc/lib/pq/pq_exec.c | 2++
Msrc/lib/pq/test_pq.c | 139++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
5 files changed, 147 insertions(+), 24 deletions(-)

diff --git a/src/lib/pq/pq.c b/src/lib/pq/pq.c @@ -114,6 +114,7 @@ GNUNET_PQ_exec_prepared (struct GNUNET_PQ_Context *db, GNUNET_PQ_reconnect_ (db); res = NULL; } + GNUNET_PQ_event_poll_ (db); for (off = 0; off < soff; off++) GNUNET_free (scratch[off]); diff --git a/src/lib/pq/pq.h b/src/lib/pq/pq.h @@ -183,4 +183,14 @@ GNUNET_PQ_event_reconnect_ (struct GNUNET_PQ_Context *db, int fd); +/** + * Schedule polling for notifications that libpq may have queued while + * executing a synchronous query. + * + * @param db the DB handle + */ +void +GNUNET_PQ_event_poll_ (struct GNUNET_PQ_Context *db); + + #endif diff --git a/src/lib/pq/pq_event.c b/src/lib/pq/pq_event.c @@ -324,6 +324,18 @@ do_poll (void *cls) } +void +GNUNET_PQ_event_poll_ (struct GNUNET_PQ_Context *db) +{ + if ( (NULL == db->conn) || + (0 == GNUNET_CONTAINER_multishortmap_size (db->channel_map)) || + (NULL != db->poller_task) ) + return; + db->poller_task = GNUNET_SCHEDULER_add_now (&do_poll, + db); +} + + /** * Function called when the Postgres FD changes and we need * to update the scheduler event loop task. @@ -604,12 +616,7 @@ GNUNET_PQ_event_notify (struct GNUNET_PQ_Context *db, Just waiting for the db socket to be readable won't work, as postgres only queues notifications we triggered for ourselves in an internal data structure. */ - if (NULL == db->poller_task) - { - db->poller_task - = GNUNET_SCHEDULER_add_now (&do_poll, - db); - } + GNUNET_PQ_event_poll_ (db); } diff --git a/src/lib/pq/pq_exec.c b/src/lib/pq/pq_exec.c @@ -84,9 +84,11 @@ GNUNET_PQ_exec_statements (struct GNUNET_PQ_Context *db, PQresStatus (PQresultStatus (result)), PQerrorMessage (db->conn)); PQclear (result); + GNUNET_PQ_event_poll_ (db); return GNUNET_SYSERR; } PQclear (result); + GNUNET_PQ_event_poll_ (db); } return GNUNET_OK; } diff --git a/src/lib/pq/test_pq.c b/src/lib/pq/test_pq.c @@ -27,6 +27,7 @@ #include "gnunet_pq_lib.h" #include "gnunet_time_lib.h" #include "pq.h" +#include <pthread.h> /** * Database handle. @@ -48,6 +49,16 @@ static struct GNUNET_DB_EventHandler *eh; */ static struct GNUNET_SCHEDULER_Task *tt; +/** + * Stage of the event notification test. + */ +static unsigned int event_stage; + +/** + * Event used by the notification tests. + */ +static struct GNUNET_DB_EventHeaderP event_spec; + /** * Setup prepared statements. @@ -154,7 +165,6 @@ postgres_prepare (struct GNUNET_PQ_Context *db_) ",arr_var_bytes" " FROM test_pq_struct_arrays" " WHERE id = $1;"), - GNUNET_PQ_PREPARED_STATEMENT_END }; @@ -1604,21 +1614,115 @@ timeout_cb (void *cls) /** - * Task called on expected event + * Context for the notification thread. + */ +struct NotifyThreadContext +{ + /** + * Postgres notification channel. + */ + char *channel; + + /** + * Set to true on success. + */ + bool success; +}; + + +/** + * Send a notification while the main connection is busy in a synchronous + * query. * - * @param cls NULL + * @param cls a `struct NotifyThreadContext *` + * @return NULL + */ +static void * +notify_during_query (void *cls) +{ + struct NotifyThreadContext *ntc = cls; + PGconn *conn; + PGresult *result; + char *sql; + + usleep (50 * 1000); + conn = PQconnectdb ("postgres:///gnunetcheck"); + if (CONNECTION_OK != PQstatus (conn)) + { + PQfinish (conn); + return NULL; + } + GNUNET_asprintf (&sql, + "NOTIFY %s", + ntc->channel); + result = PQexec (conn, + sql); + ntc->success = (PGRES_COMMAND_OK == PQresultStatus (result)); + PQclear (result); + GNUNET_free (sql); + PQfinish (conn); + return NULL; +} + + +/** + * Run a synchronous query while another connection sends a notification. + * + * @param cls a `struct GNUNET_DB_EventHeaderP *` + */ +static void +test_event_during_query (void *cls) +{ + const struct GNUNET_DB_EventHeaderP *es = cls; + struct NotifyThreadContext ntc; + struct GNUNET_PQ_ExecuteStatement statements[] = { + GNUNET_PQ_make_execute ( + "DO $$ BEGIN PERFORM pg_sleep(0.25); END $$;"), + GNUNET_PQ_EXECUTE_STATEMENT_END + }; + pthread_t thread; + + ntc.channel = GNUNET_PQ_get_event_notify_channel (es); + ntc.success = false; + GNUNET_assert (0 == + pthread_create (&thread, + NULL, + &notify_during_query, + &ntc)); + GNUNET_assert (GNUNET_OK == + GNUNET_PQ_exec_statements (db, + statements)); + GNUNET_assert (0 == + pthread_join (thread, + NULL)); + GNUNET_assert (ntc.success); + GNUNET_free (ntc.channel); +} + + +/** + * Task called on expected event. + * + * @param cls a `struct GNUNET_DB_EventHeaderP *` */ static void event_sched_cb (void *cls, const void *extra, size_t extra_size) { - (void) cls; - GNUNET_assert (5 == extra_size); - GNUNET_assert (0 == - memcmp ("hello", - extra, - 5)); + if (0 == event_stage) + { + GNUNET_assert (5 == extra_size); + GNUNET_assert (0 == + memcmp ("hello", + extra, + 5)); + event_stage++; + GNUNET_SCHEDULER_add_now (&test_event_during_query, + cls); + return; + } + GNUNET_assert (0 == extra_size); GNUNET_SCHEDULER_shutdown (); } @@ -1631,25 +1735,24 @@ event_sched_cb (void *cls, static void sched_tests (void *cls) { - struct GNUNET_DB_EventHeaderP es = { - .size = htons (sizeof (es)), - .type = htons (42) - }; - (void) cls; - tt = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, + event_spec.size = htons (sizeof (event_spec)); + event_spec.type = htons (42); + tt = GNUNET_SCHEDULER_add_delayed ( + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, + 2), &timeout_cb, NULL); eh = GNUNET_PQ_event_listen (db, - &es, + &event_spec, GNUNET_TIME_UNIT_FOREVER_REL, &event_sched_cb, - NULL); + &event_spec); GNUNET_PQ_reconnect_ (db); GNUNET_SCHEDULER_add_shutdown (&event_end, NULL); GNUNET_PQ_event_notify (db, - &es, + &event_spec, "hello", 5); }