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

          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          14 : TALER_FAKEBANK_lp_expiration_thread_ (void *cls)
      68             : {
      69          14 :   struct TALER_FAKEBANK_Handle *h = cls;
      70             : 
      71          14 :   GNUNET_assert (0 ==
      72             :                  pthread_mutex_lock (&h->big_lock));
      73          28 :   while (! h->in_shutdown)
      74             :   {
      75             :     struct LongPoller *lp;
      76             :     int timeout_ms;
      77             : 
      78          14 :     lp = GNUNET_CONTAINER_heap_peek (h->lp_heap);
      79          14 :     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          14 :     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          14 :       timeout_ms = -1; /* infinity */
     102             :     }
     103          14 :     GNUNET_assert (0 ==
     104             :                    pthread_mutex_unlock (&h->big_lock));
     105             :     {
     106          14 :       struct pollfd p = {
     107             : #ifdef __linux__
     108          14 :         .fd = h->lp_event,
     109             : #else
     110             :         .fd = h->lp_event_out,
     111             : #endif
     112             :         .events = POLLIN
     113             :       };
     114             :       int ret;
     115             : 
     116          14 :       ret = poll (&p,
     117             :                   1,
     118             :                   timeout_ms);
     119          14 :       if (-1 == ret)
     120             :       {
     121           0 :         if (EINTR != errno)
     122           0 :           GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
     123             :                                "poll");
     124             :       }
     125          14 :       else if (1 == ret)
     126             :       {
     127             :         /* clear event */
     128             :         uint64_t ev;
     129             :         ssize_t iret;
     130             : 
     131             : #ifdef __linux__
     132          14 :         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          14 :         if (-1 == iret)
     141             :         {
     142           0 :           GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
     143             :                                "read");
     144             :         }
     145             :         else
     146             :         {
     147          14 :           GNUNET_break (sizeof (uint64_t) == iret);
     148             :         }
     149             :       }
     150             :     }
     151          14 :     GNUNET_assert (0 ==
     152             :                    pthread_mutex_lock (&h->big_lock));
     153             :   }
     154          14 :   GNUNET_assert (0 ==
     155             :                  pthread_mutex_unlock (&h->big_lock));
     156          14 :   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         127 : TALER_FAKEBANK_notify_transaction_ (
     169             :   struct TALER_FAKEBANK_Handle *h,
     170             :   struct Transaction *t)
     171             : {
     172         127 :   struct Account *debit_acc = t->debit_account;
     173         127 :   struct Account *credit_acc = t->credit_account;
     174             :   struct LongPoller *nxt;
     175             : 
     176         127 :   GNUNET_assert (0 ==
     177             :                  pthread_mutex_lock (&h->big_lock));
     178         127 :   for (struct LongPoller *lp = debit_acc->lp_head;
     179         127 :        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         127 :   for (struct LongPoller *lp = credit_acc->lp_head;
     191         127 :        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         127 :   GNUNET_assert (0 ==
     203             :                  pthread_mutex_unlock (&h->big_lock));
     204         127 : }
     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 1.16