commit 09dc2f98a23a3fe0fdd0405f62ad4fe49c88cbea
parent 767ef1d06b2de26559d370bd26d398bedc18f554
Author: Christian Grothoff <christian@grothoff.org>
Date: Thu, 13 Aug 2026 20:15:05 +0200
ensure au cleanup on shutdown
Diffstat:
1 file changed, 35 insertions(+), 6 deletions(-)
diff --git a/src/exchange/taler-exchange-aggregator.c b/src/exchange/taler-exchange-aggregator.c
@@ -244,6 +244,15 @@ static struct TALER_EXCHANGEDB_PostgresContext *pg;
static struct GNUNET_SCHEDULER_Task *task;
/**
+ * The aggregation we are currently working on, NULL if none. There is at most
+ * one at any point in time, as the next one is only started once the previous
+ * one was cleaned up. Needed so that we can cancel an asynchronous rule
+ * update and free the aggregation on shutdown, as the rule update outlives
+ * the task that started it and keeps a reference to @e pg.
+ */
+static struct AggregationUnit *au_active;
+
+/**
* How long should we sleep when idle before trying to find more work?
*/
static struct GNUNET_TIME_Relative aggregator_idle_sleep_interval;
@@ -294,6 +303,8 @@ static void
cleanup_au (struct AggregationUnit *au)
{
GNUNET_assert (NULL != au);
+ if (au == au_active)
+ au_active = NULL;
GNUNET_free (au->extra_wire_subject_metadata);
if (NULL != au->ru)
{
@@ -439,6 +450,20 @@ shutdown_task (void *cls)
GNUNET_SCHEDULER_cancel (task);
task = NULL;
}
+ if (NULL != au_active)
+ {
+ struct AggregationUnit *au = au_active;
+
+ /* An asynchronous rule update is not a shutdown task and would thus
+ still run (on the freed database context) after we return; stop it
+ while the KYC logic and the database are still around. */
+ if (NULL != au->ru)
+ {
+ TALER_EXCHANGEDB_begin_rule_update_cancel (au->ru);
+ au->ru = NULL;
+ }
+ cleanup_au (au);
+ }
TALER_KYCLOGIC_kyc_done ();
TALER_EXCHANGEDB_disconnect (pg);
pg = NULL;
@@ -1300,6 +1325,8 @@ run_aggregation (void *cls)
"Checking for ready deposits to aggregate\n");
/* make sure we have current fees */
au = GNUNET_new (struct AggregationUnit);
+ GNUNET_assert (NULL == au_active);
+ au_active = au;
au->execution_time = GNUNET_TIME_timestamp_get ();
au->shard = s;
if (GNUNET_OK !=
@@ -1450,13 +1477,15 @@ drain_kyc_alerts (void *cls)
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Draining KYC alerts\n");
au = GNUNET_new (struct AggregationUnit);
+ GNUNET_assert (NULL == au_active);
+ au_active = au;
au->execution_time = GNUNET_TIME_timestamp_get ();
if (GNUNET_SYSERR ==
TALER_EXCHANGEDB_preflight (pg))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to obtain database connection!\n");
- GNUNET_free (au);
+ cleanup_au (au);
global_ret = EXIT_FAILURE;
GNUNET_SCHEDULER_shutdown ();
return;
@@ -1467,7 +1496,7 @@ drain_kyc_alerts (void *cls)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to start database transaction!\n");
- GNUNET_free (au);
+ cleanup_au (au);
global_ret = EXIT_FAILURE;
GNUNET_SCHEDULER_shutdown ();
return;
@@ -1485,7 +1514,7 @@ drain_kyc_alerts (void *cls)
case GNUNET_DB_STATUS_HARD_ERROR:
GNUNET_break (0);
TALER_EXCHANGEDB_rollback (pg);
- GNUNET_free (au);
+ cleanup_au (au);
GNUNET_assert (NULL == task);
global_ret = EXIT_FAILURE;
GNUNET_SCHEDULER_shutdown ();
@@ -1493,12 +1522,12 @@ drain_kyc_alerts (void *cls)
case GNUNET_DB_STATUS_SOFT_ERROR:
TALER_EXCHANGEDB_rollback (pg);
GNUNET_assert (NULL == task);
- GNUNET_free (au);
+ cleanup_au (au);
task = GNUNET_SCHEDULER_add_now (&drain_kyc_alerts,
NULL);
return;
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
- GNUNET_free (au);
+ cleanup_au (au);
TALER_EXCHANGEDB_rollback (pg);
GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&run_shard,
@@ -1522,7 +1551,7 @@ drain_kyc_alerts (void *cls)
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to lookup transient aggregates!\n");
TALER_EXCHANGEDB_rollback (pg);
- GNUNET_free (au);
+ cleanup_au (au);
global_ret = EXIT_FAILURE;
GNUNET_SCHEDULER_shutdown ();
return;