From 48e58124fb61ded372f147d00d112f108c997f81 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Thu, 21 Sep 2023 00:01:45 +0200 Subject: major refactor of fakebank implementation --- src/bank-lib/fakebank_common_lp.c | 344 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 344 insertions(+) create mode 100644 src/bank-lib/fakebank_common_lp.c (limited to 'src/bank-lib/fakebank_common_lp.c') diff --git a/src/bank-lib/fakebank_common_lp.c b/src/bank-lib/fakebank_common_lp.c new file mode 100644 index 000000000..22a9e3ab4 --- /dev/null +++ b/src/bank-lib/fakebank_common_lp.c @@ -0,0 +1,344 @@ +/* + This file is part of TALER + (C) 2016-2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 3, + or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with TALER; see the file COPYING. If not, + see +*/ +/** + * @file bank-lib/fakebank_common_lp.c + * @brief long-polling support for fakebank + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#ifdef __linux__ +#include +#endif +#include "taler_fakebank_lib.h" +#include "taler_bank_service.h" +#include "taler_mhd_lib.h" +#include +#include "fakebank.h" + + +void +TALER_FAKEBANK_lp_trigger_ (struct LongPoller *lp) +{ + struct TALER_FAKEBANK_Handle *h = lp->h; + struct Account *acc = lp->account; + + GNUNET_CONTAINER_DLL_remove (acc->lp_head, + acc->lp_tail, + lp); + MHD_resume_connection (lp->conn); + GNUNET_free (lp); + h->mhd_again = true; +#ifdef __linux__ + if (-1 == h->lp_event) +#else + if ( (-1 == h->lp_event_in) && + (-1 == h->lp_event_out) ) +#endif + { + if (NULL != h->mhd_task) + GNUNET_SCHEDULER_cancel (h->mhd_task); + h->mhd_task = + GNUNET_SCHEDULER_add_now (&TALER_FAKEBANK_run_mhd_, + h); + } +} + + +void * +TALER_FAKEBANK_lp_expiration_thread_ (void *cls) +{ + struct TALER_FAKEBANK_Handle *h = cls; + + GNUNET_assert (0 == + pthread_mutex_lock (&h->big_lock)); + while (! h->in_shutdown) + { + struct LongPoller *lp; + int timeout_ms; + + lp = GNUNET_CONTAINER_heap_peek (h->lp_heap); + while ( (NULL != lp) && + GNUNET_TIME_absolute_is_past (lp->timeout)) + { + GNUNET_assert (lp == + GNUNET_CONTAINER_heap_remove_root (h->lp_heap)); + TALER_FAKEBANK_lp_trigger_ (lp); + lp = GNUNET_CONTAINER_heap_peek (h->lp_heap); + } + if (NULL != lp) + { + struct GNUNET_TIME_Relative rem; + unsigned long long left_ms; + + rem = GNUNET_TIME_absolute_get_remaining (lp->timeout); + left_ms = rem.rel_value_us / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us; + if (left_ms > INT_MAX) + timeout_ms = INT_MAX; + else + timeout_ms = (int) left_ms; + } + else + { + timeout_ms = -1; /* infinity */ + } + GNUNET_assert (0 == + pthread_mutex_unlock (&h->big_lock)); + { + struct pollfd p = { +#ifdef __linux__ + .fd = h->lp_event, +#else + .fd = h->lp_event_out, +#endif + .events = POLLIN + }; + int ret; + + ret = poll (&p, + 1, + timeout_ms); + if (-1 == ret) + { + if (EINTR != errno) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "poll"); + } + else if (1 == ret) + { + /* clear event */ + uint64_t ev; + ssize_t iret; + +#ifdef __linux__ + iret = read (h->lp_event, + &ev, + sizeof (ev)); +#else + iret = read (h->lp_event_out, + &ev, + sizeof (ev)); +#endif + if (-1 == iret) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "read"); + } + else + { + GNUNET_break (sizeof (uint64_t) == iret); + } + } + } + GNUNET_assert (0 == + pthread_mutex_lock (&h->big_lock)); + } + GNUNET_assert (0 == + pthread_mutex_unlock (&h->big_lock)); + return NULL; +} + + +/** + * Trigger long pollers that might have been waiting + * for @a t. + * + * @param h fakebank handle + * @param t transaction to notify on + */ +void +TALER_FAKEBANK_notify_transaction_ ( + struct TALER_FAKEBANK_Handle *h, + struct Transaction *t) +{ + struct Account *debit_acc = t->debit_account; + struct Account *credit_acc = t->credit_account; + struct LongPoller *nxt; + + GNUNET_assert (0 == + pthread_mutex_lock (&h->big_lock)); + for (struct LongPoller *lp = debit_acc->lp_head; + NULL != lp; + lp = nxt) + { + nxt = lp->next; + if (LP_DEBIT == lp->type) + { + GNUNET_assert (lp == + GNUNET_CONTAINER_heap_remove_node (lp->hn)); + TALER_FAKEBANK_lp_trigger_ (lp); + } + } + for (struct LongPoller *lp = credit_acc->lp_head; + NULL != lp; + lp = nxt) + { + nxt = lp->next; + if (LP_CREDIT == lp->type) + { + GNUNET_assert (lp == + GNUNET_CONTAINER_heap_remove_node (lp->hn)); + TALER_FAKEBANK_lp_trigger_ (lp); + } + } + GNUNET_assert (0 == + pthread_mutex_unlock (&h->big_lock)); +} + + +/** + * Notify long pollers that a @a wo was updated. + * Must be called with the "big_lock" still held. + * + * @param h fakebank handle + * @param wo withdraw operation that finished + */ +void +TALER_FAKEBANK_notify_withdrawal_ ( + struct TALER_FAKEBANK_Handle *h, + const struct WithdrawalOperation *wo) +{ + struct Account *debit_acc = wo->debit_account; + struct LongPoller *nxt; + + for (struct LongPoller *lp = debit_acc->lp_head; + NULL != lp; + lp = nxt) + { + nxt = lp->next; + if ( (LP_WITHDRAW == lp->type) && + (wo == lp->wo) ) + { + GNUNET_assert (lp == + GNUNET_CONTAINER_heap_remove_node (lp->hn)); + TALER_FAKEBANK_lp_trigger_ (lp); + } + } +} + + +/** + * Task run when a long poller is about to time out. + * Only used in single-threaded mode. + * + * @param cls a `struct TALER_FAKEBANK_Handle *` + */ +static void +lp_timeout (void *cls) +{ + struct TALER_FAKEBANK_Handle *h = cls; + struct LongPoller *lp; + + h->lp_task = NULL; + while (NULL != (lp = GNUNET_CONTAINER_heap_peek (h->lp_heap))) + { + if (GNUNET_TIME_absolute_is_future (lp->timeout)) + break; + GNUNET_assert (lp == + GNUNET_CONTAINER_heap_remove_root (h->lp_heap)); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Timeout reached for long poller %p\n", + lp->conn); + TALER_FAKEBANK_lp_trigger_ (lp); + } + if (NULL == lp) + return; + h->lp_task = GNUNET_SCHEDULER_add_at (lp->timeout, + &lp_timeout, + h); +} + + +/** + * Reschedule the timeout task of @a h for time @a t. + * + * @param h fakebank handle + * @param t when will the next connection timeout expire + */ +static void +reschedule_lp_timeout (struct TALER_FAKEBANK_Handle *h, + struct GNUNET_TIME_Absolute t) +{ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Scheduling timeout task for %s\n", + GNUNET_STRINGS_absolute_time_to_string (t)); +#ifdef __linux__ + if (-1 != h->lp_event) +#else + if (-1 != h->lp_event_in && -1 != h->lp_event_out) +#endif + { + uint64_t num = 1; + + GNUNET_break (sizeof (num) == +#ifdef __linux__ + write (h->lp_event, + &num, + sizeof (num))); +#else + write (h->lp_event_in, + &num, + sizeof (num))); +#endif + } + else + { + if (NULL != h->lp_task) + GNUNET_SCHEDULER_cancel (h->lp_task); + h->lp_task = GNUNET_SCHEDULER_add_at (t, + &lp_timeout, + h); + } +} + + +void +TALER_FAKEBANK_start_lp_ ( + struct TALER_FAKEBANK_Handle *h, + struct MHD_Connection *connection, + struct Account *acc, + struct GNUNET_TIME_Relative lp_timeout, + enum LongPollType dir, + const struct WithdrawalOperation *wo) +{ + struct LongPoller *lp; + bool toc; + + lp = GNUNET_new (struct LongPoller); + lp->account = acc; + lp->h = h; + lp->wo = wo; + lp->conn = connection; + lp->timeout = GNUNET_TIME_relative_to_absolute (lp_timeout); + lp->type = dir; + lp->hn = GNUNET_CONTAINER_heap_insert (h->lp_heap, + lp, + lp->timeout.abs_value_us); + toc = (lp == + GNUNET_CONTAINER_heap_peek (h->lp_heap)); + GNUNET_CONTAINER_DLL_insert (acc->lp_head, + acc->lp_tail, + lp); + MHD_suspend_connection (connection); + if (toc) + reschedule_lp_timeout (h, + lp->timeout); + +} -- cgit v1.2.3