LCOV - code coverage report
Current view: top level - bank-lib - fakebank_common_lp.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 26.5 % 113 30
Test Date: 2026-01-12 22:36:41 Functions: 28.6 % 7 2

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   (C) 2016-2023 Taler Systems SA
       4              : 
       5              :   TALER is free software; you can redistribute it and/or
       6              :   modify it under the terms of the GNU General Public License
       7              :   as published by the Free Software Foundation; either version 3,
       8              :   or (at your option) any later version.
       9              : 
      10              :   TALER is distributed in the hope that it will be useful,
      11              :   but WITHOUT ANY WARRANTY; without even the implied warranty of
      12              :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13              :   GNU General Public License for more details.
      14              : 
      15              :   You should have received a copy of the GNU General Public
      16              :   License along with TALER; see the file COPYING.  If not,
      17              :   see <http://www.gnu.org/licenses/>
      18              : */
      19              : /**
      20              :  * @file bank-lib/fakebank_common_lp.c
      21              :  * @brief long-polling support for fakebank
      22              :  * @author Christian Grothoff <christian@grothoff.org>
      23              :  */
      24              : #include "taler/platform.h"
      25              : #include <pthread.h>
      26              : #include <poll.h>
      27              : #ifdef __linux__
      28              : #include <sys/eventfd.h>
      29              : #endif
      30              : #include "taler/taler_fakebank_lib.h"
      31              : #include "taler/taler_bank_service.h"
      32              : #include "taler/taler_mhd_lib.h"
      33              : #include <gnunet/gnunet_mhd_compat.h>
      34              : #include "fakebank.h"
      35              : #include "fakebank_common_lp.h"
      36              : 
      37              : 
      38              : void
      39            0 : TALER_FAKEBANK_lp_trigger_ (struct LongPoller *lp)
      40              : {
      41            0 :   struct TALER_FAKEBANK_Handle *h = lp->h;
      42            0 :   struct Account *acc = lp->account;
      43              : 
      44            0 :   GNUNET_CONTAINER_DLL_remove (acc->lp_head,
      45              :                                acc->lp_tail,
      46              :                                lp);
      47            0 :   MHD_resume_connection (lp->conn);
      48            0 :   GNUNET_free (lp);
      49            0 :   h->mhd_again = true;
      50              : #ifdef __linux__
      51            0 :   if (-1 == h->lp_event)
      52              : #else
      53              :   if ( (-1 == h->lp_event_in) &&
      54              :        (-1 == h->lp_event_out) )
      55              : #endif
      56              :   {
      57            0 :     if (NULL != h->mhd_task)
      58            0 :       GNUNET_SCHEDULER_cancel (h->mhd_task);
      59            0 :     h->mhd_task =
      60            0 :       GNUNET_SCHEDULER_add_now (&TALER_FAKEBANK_run_mhd_,
      61              :                                 h);
      62              :   }
      63            0 : }
      64              : 
      65              : 
      66              : void *
      67           15 : TALER_FAKEBANK_lp_expiration_thread_ (void *cls)
      68              : {
      69           15 :   struct TALER_FAKEBANK_Handle *h = cls;
      70              : 
      71           15 :   GNUNET_assert (0 ==
      72              :                  pthread_mutex_lock (&h->big_lock));
      73           30 :   while (! h->in_shutdown)
      74              :   {
      75              :     struct LongPoller *lp;
      76              :     int timeout_ms;
      77              : 
      78           15 :     lp = GNUNET_CONTAINER_heap_peek (h->lp_heap);
      79           15 :     while ( (NULL != lp) &&
      80            0 :             GNUNET_TIME_absolute_is_past (lp->timeout))
      81              :     {
      82            0 :       GNUNET_assert (lp ==
      83              :                      GNUNET_CONTAINER_heap_remove_root (h->lp_heap));
      84            0 :       TALER_FAKEBANK_lp_trigger_ (lp);
      85            0 :       lp = GNUNET_CONTAINER_heap_peek (h->lp_heap);
      86              :     }
      87           15 :     if (NULL != lp)
      88              :     {
      89              :       struct GNUNET_TIME_Relative rem;
      90              :       unsigned long long left_ms;
      91              : 
      92            0 :       rem = GNUNET_TIME_absolute_get_remaining (lp->timeout);
      93            0 :       left_ms = rem.rel_value_us / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us;
      94            0 :       if (left_ms > INT_MAX)
      95            0 :         timeout_ms = INT_MAX;
      96              :       else
      97            0 :         timeout_ms = (int) left_ms;
      98              :     }
      99              :     else
     100              :     {
     101           15 :       timeout_ms = -1; /* infinity */
     102              :     }
     103           15 :     GNUNET_assert (0 ==
     104              :                    pthread_mutex_unlock (&h->big_lock));
     105              :     {
     106           15 :       struct pollfd p = {
     107              : #ifdef __linux__
     108           15 :         .fd = h->lp_event,
     109              : #else
     110              :         .fd = h->lp_event_out,
     111              : #endif
     112              :         .events = POLLIN
     113              :       };
     114              :       int ret;
     115              : 
     116           15 :       ret = poll (&p,
     117              :                   1,
     118              :                   timeout_ms);
     119           15 :       if (-1 == ret)
     120              :       {
     121            0 :         if (EINTR != errno)
     122            0 :           GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
     123              :                                "poll");
     124              :       }
     125           15 :       else if (1 == ret)
     126              :       {
     127              :         /* clear event */
     128              :         uint64_t ev;
     129              :         ssize_t iret;
     130              : 
     131              : #ifdef __linux__
     132           15 :         iret = read (h->lp_event,
     133              :                      &ev,
     134              :                      sizeof (ev));
     135              : #else
     136              :         iret = read (h->lp_event_out,
     137              :                      &ev,
     138              :                      sizeof (ev));
     139              : #endif
     140           15 :         if (-1 == iret)
     141              :         {
     142            0 :           GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
     143              :                                "read");
     144              :         }
     145              :         else
     146              :         {
     147           15 :           GNUNET_break (sizeof (uint64_t) == iret);
     148              :         }
     149              :       }
     150              :     }
     151           15 :     GNUNET_assert (0 ==
     152              :                    pthread_mutex_lock (&h->big_lock));
     153              :   }
     154           15 :   GNUNET_assert (0 ==
     155              :                  pthread_mutex_unlock (&h->big_lock));
     156           15 :   return NULL;
     157              : }
     158              : 
     159              : 
     160              : /**
     161              :  * Trigger long pollers that might have been waiting
     162              :  * for @a t.
     163              :  *
     164              :  * @param h fakebank handle
     165              :  * @param t transaction to notify on
     166              :  */
     167              : void
     168          130 : TALER_FAKEBANK_notify_transaction_ (
     169              :   struct TALER_FAKEBANK_Handle *h,
     170              :   struct Transaction *t)
     171              : {
     172          130 :   struct Account *debit_acc = t->debit_account;
     173          130 :   struct Account *credit_acc = t->credit_account;
     174              :   struct LongPoller *nxt;
     175              : 
     176          130 :   GNUNET_assert (0 ==
     177              :                  pthread_mutex_lock (&h->big_lock));
     178          130 :   for (struct LongPoller *lp = debit_acc->lp_head;
     179          130 :        NULL != lp;
     180            0 :        lp = nxt)
     181              :   {
     182            0 :     nxt = lp->next;
     183            0 :     if (LP_DEBIT == lp->type)
     184              :     {
     185            0 :       GNUNET_assert (lp ==
     186              :                      GNUNET_CONTAINER_heap_remove_node (lp->hn));
     187            0 :       TALER_FAKEBANK_lp_trigger_ (lp);
     188              :     }
     189              :   }
     190          130 :   for (struct LongPoller *lp = credit_acc->lp_head;
     191          130 :        NULL != lp;
     192            0 :        lp = nxt)
     193              :   {
     194            0 :     nxt = lp->next;
     195            0 :     if (LP_CREDIT == lp->type)
     196              :     {
     197            0 :       GNUNET_assert (lp ==
     198              :                      GNUNET_CONTAINER_heap_remove_node (lp->hn));
     199            0 :       TALER_FAKEBANK_lp_trigger_ (lp);
     200              :     }
     201              :   }
     202          130 :   GNUNET_assert (0 ==
     203              :                  pthread_mutex_unlock (&h->big_lock));
     204          130 : }
     205              : 
     206              : 
     207              : /**
     208              :  * Notify long pollers that a @a wo was updated.
     209              :  * Must be called with the "big_lock" still held.
     210              :  *
     211              :  * @param h fakebank handle
     212              :  * @param wo withdraw operation that finished
     213              :  */
     214              : void
     215            0 : TALER_FAKEBANK_notify_withdrawal_ (
     216              :   struct TALER_FAKEBANK_Handle *h,
     217              :   const struct WithdrawalOperation *wo)
     218              : {
     219            0 :   struct Account *debit_acc = wo->debit_account;
     220              :   struct LongPoller *nxt;
     221              : 
     222            0 :   for (struct LongPoller *lp = debit_acc->lp_head;
     223            0 :        NULL != lp;
     224            0 :        lp = nxt)
     225              :   {
     226            0 :     nxt = lp->next;
     227            0 :     if ( (LP_WITHDRAW == lp->type) &&
     228            0 :          (wo == lp->wo) )
     229              :     {
     230            0 :       GNUNET_assert (lp ==
     231              :                      GNUNET_CONTAINER_heap_remove_node (lp->hn));
     232            0 :       TALER_FAKEBANK_lp_trigger_ (lp);
     233              :     }
     234              :   }
     235            0 : }
     236              : 
     237              : 
     238              : /**
     239              :  * Task run when a long poller is about to time out.
     240              :  * Only used in single-threaded mode.
     241              :  *
     242              :  * @param cls a `struct TALER_FAKEBANK_Handle *`
     243              :  */
     244              : static void
     245            0 : lp_timeout (void *cls)
     246              : {
     247            0 :   struct TALER_FAKEBANK_Handle *h = cls;
     248              :   struct LongPoller *lp;
     249              : 
     250            0 :   h->lp_task = NULL;
     251            0 :   while (NULL != (lp = GNUNET_CONTAINER_heap_peek (h->lp_heap)))
     252              :   {
     253            0 :     if (GNUNET_TIME_absolute_is_future (lp->timeout))
     254            0 :       break;
     255            0 :     GNUNET_assert (lp ==
     256              :                    GNUNET_CONTAINER_heap_remove_root (h->lp_heap));
     257            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     258              :                 "Timeout reached for long poller %p\n",
     259              :                 lp->conn);
     260            0 :     TALER_FAKEBANK_lp_trigger_ (lp);
     261              :   }
     262            0 :   if (NULL == lp)
     263            0 :     return;
     264            0 :   h->lp_task = GNUNET_SCHEDULER_add_at (lp->timeout,
     265              :                                         &lp_timeout,
     266              :                                         h);
     267              : }
     268              : 
     269              : 
     270              : /**
     271              :  * Reschedule the timeout task of @a h for time @a t.
     272              :  *
     273              :  * @param h fakebank handle
     274              :  * @param t when will the next connection timeout expire
     275              :  */
     276              : static void
     277            0 : reschedule_lp_timeout (struct TALER_FAKEBANK_Handle *h,
     278              :                        struct GNUNET_TIME_Absolute t)
     279              : {
     280            0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     281              :               "Scheduling timeout task for %s\n",
     282              :               GNUNET_STRINGS_absolute_time_to_string (t));
     283              : #ifdef __linux__
     284            0 :   if (-1 != h->lp_event)
     285              : #else
     286              :   if (-1 != h->lp_event_in && -1 != h->lp_event_out)
     287              : #endif
     288              :   {
     289            0 :     uint64_t num = 1;
     290              : 
     291            0 :     GNUNET_break (sizeof (num) ==
     292              : #ifdef __linux__
     293              :                   write (h->lp_event,
     294              :                          &num,
     295              :                          sizeof (num)));
     296              : #else
     297              :                   write (h->lp_event_in,
     298              :                          &num,
     299              :                          sizeof (num)));
     300              : #endif
     301              :   }
     302              :   else
     303              :   {
     304            0 :     if (NULL != h->lp_task)
     305            0 :       GNUNET_SCHEDULER_cancel (h->lp_task);
     306            0 :     h->lp_task = GNUNET_SCHEDULER_add_at (t,
     307              :                                           &lp_timeout,
     308              :                                           h);
     309              :   }
     310            0 : }
     311              : 
     312              : 
     313              : void
     314            0 : TALER_FAKEBANK_start_lp_ (
     315              :   struct TALER_FAKEBANK_Handle *h,
     316              :   struct MHD_Connection *connection,
     317              :   struct Account *acc,
     318              :   struct GNUNET_TIME_Relative lp_timeout,
     319              :   enum LongPollType dir,
     320              :   const struct WithdrawalOperation *wo)
     321              : {
     322              :   struct LongPoller *lp;
     323              :   bool toc;
     324              : 
     325            0 :   lp = GNUNET_new (struct LongPoller);
     326            0 :   lp->account = acc;
     327            0 :   lp->h = h;
     328            0 :   lp->wo = wo;
     329            0 :   lp->conn = connection;
     330            0 :   lp->timeout = GNUNET_TIME_relative_to_absolute (lp_timeout);
     331            0 :   lp->type = dir;
     332            0 :   lp->hn = GNUNET_CONTAINER_heap_insert (h->lp_heap,
     333              :                                          lp,
     334              :                                          lp->timeout.abs_value_us);
     335            0 :   toc = (lp ==
     336            0 :          GNUNET_CONTAINER_heap_peek (h->lp_heap));
     337            0 :   GNUNET_CONTAINER_DLL_insert (acc->lp_head,
     338              :                                acc->lp_tail,
     339              :                                lp);
     340            0 :   MHD_suspend_connection (connection);
     341            0 :   if (toc)
     342            0 :     reschedule_lp_timeout (h,
     343              :                            lp->timeout);
     344              : 
     345            0 : }
        

Generated by: LCOV version 2.0-1