exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

fakebank_common_lp.c (8750B)


      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 TALER_FAKEBANK_lp_trigger_ (struct LongPoller *lp)
     40 {
     41   struct TALER_FAKEBANK_Handle *h = lp->h;
     42   struct Account *acc = lp->account;
     43 
     44   GNUNET_CONTAINER_DLL_remove (acc->lp_head,
     45                                acc->lp_tail,
     46                                lp);
     47   MHD_resume_connection (lp->conn);
     48   GNUNET_free (lp);
     49   h->mhd_again = true;
     50 #ifdef __linux__
     51   if (-1 == h->lp_event)
     52 #else
     53   if ( (-1 == h->lp_event_in) &&
     54        (-1 == h->lp_event_out) )
     55 #endif
     56   {
     57     if (NULL != h->mhd_task)
     58       GNUNET_SCHEDULER_cancel (h->mhd_task);
     59     h->mhd_task =
     60       GNUNET_SCHEDULER_add_now (&TALER_FAKEBANK_run_mhd_,
     61                                 h);
     62   }
     63 }
     64 
     65 
     66 void *
     67 TALER_FAKEBANK_lp_expiration_thread_ (void *cls)
     68 {
     69   struct TALER_FAKEBANK_Handle *h = cls;
     70 
     71   GNUNET_assert (0 ==
     72                  pthread_mutex_lock (&h->big_lock));
     73   while (! h->in_shutdown)
     74   {
     75     struct LongPoller *lp;
     76     int timeout_ms;
     77 
     78     lp = GNUNET_CONTAINER_heap_peek (h->lp_heap);
     79     while ( (NULL != lp) &&
     80             GNUNET_TIME_absolute_is_past (lp->timeout))
     81     {
     82       GNUNET_assert (lp ==
     83                      GNUNET_CONTAINER_heap_remove_root (h->lp_heap));
     84       TALER_FAKEBANK_lp_trigger_ (lp);
     85       lp = GNUNET_CONTAINER_heap_peek (h->lp_heap);
     86     }
     87     if (NULL != lp)
     88     {
     89       struct GNUNET_TIME_Relative rem;
     90       unsigned long long left_ms;
     91 
     92       rem = GNUNET_TIME_absolute_get_remaining (lp->timeout);
     93       left_ms = rem.rel_value_us / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us;
     94       if (left_ms > INT_MAX)
     95         timeout_ms = INT_MAX;
     96       else
     97         timeout_ms = (int) left_ms;
     98     }
     99     else
    100     {
    101       timeout_ms = -1; /* infinity */
    102     }
    103     GNUNET_assert (0 ==
    104                    pthread_mutex_unlock (&h->big_lock));
    105     {
    106       struct pollfd p = {
    107 #ifdef __linux__
    108         .fd = h->lp_event,
    109 #else
    110         .fd = h->lp_event_out,
    111 #endif
    112         .events = POLLIN
    113       };
    114       int ret;
    115 
    116       ret = poll (&p,
    117                   1,
    118                   timeout_ms);
    119       if (-1 == ret)
    120       {
    121         if (EINTR != errno)
    122           GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
    123                                "poll");
    124       }
    125       else if (1 == ret)
    126       {
    127         /* clear event */
    128         uint64_t ev;
    129         ssize_t iret;
    130 
    131 #ifdef __linux__
    132         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         if (-1 == iret)
    141         {
    142           GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
    143                                "read");
    144         }
    145         else
    146         {
    147           GNUNET_break (sizeof (uint64_t) == iret);
    148         }
    149       }
    150     }
    151     GNUNET_assert (0 ==
    152                    pthread_mutex_lock (&h->big_lock));
    153   }
    154   GNUNET_assert (0 ==
    155                  pthread_mutex_unlock (&h->big_lock));
    156   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 TALER_FAKEBANK_notify_transaction_ (
    169   struct TALER_FAKEBANK_Handle *h,
    170   struct Transaction *t)
    171 {
    172   struct Account *debit_acc = t->debit_account;
    173   struct Account *credit_acc = t->credit_account;
    174   struct LongPoller *nxt;
    175 
    176   GNUNET_assert (0 ==
    177                  pthread_mutex_lock (&h->big_lock));
    178   for (struct LongPoller *lp = debit_acc->lp_head;
    179        NULL != lp;
    180        lp = nxt)
    181   {
    182     nxt = lp->next;
    183     if (LP_DEBIT == lp->type)
    184     {
    185       GNUNET_assert (lp ==
    186                      GNUNET_CONTAINER_heap_remove_node (lp->hn));
    187       TALER_FAKEBANK_lp_trigger_ (lp);
    188     }
    189   }
    190   for (struct LongPoller *lp = credit_acc->lp_head;
    191        NULL != lp;
    192        lp = nxt)
    193   {
    194     nxt = lp->next;
    195     if (LP_CREDIT == lp->type)
    196     {
    197       GNUNET_assert (lp ==
    198                      GNUNET_CONTAINER_heap_remove_node (lp->hn));
    199       TALER_FAKEBANK_lp_trigger_ (lp);
    200     }
    201   }
    202   GNUNET_assert (0 ==
    203                  pthread_mutex_unlock (&h->big_lock));
    204 }
    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 TALER_FAKEBANK_notify_withdrawal_ (
    216   struct TALER_FAKEBANK_Handle *h,
    217   const struct WithdrawalOperation *wo)
    218 {
    219   struct Account *debit_acc = wo->debit_account;
    220   struct LongPoller *nxt;
    221 
    222   for (struct LongPoller *lp = debit_acc->lp_head;
    223        NULL != lp;
    224        lp = nxt)
    225   {
    226     nxt = lp->next;
    227     if ( (LP_WITHDRAW == lp->type) &&
    228          (wo == lp->wo) )
    229     {
    230       GNUNET_assert (lp ==
    231                      GNUNET_CONTAINER_heap_remove_node (lp->hn));
    232       TALER_FAKEBANK_lp_trigger_ (lp);
    233     }
    234   }
    235 }
    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 lp_timeout (void *cls)
    246 {
    247   struct TALER_FAKEBANK_Handle *h = cls;
    248   struct LongPoller *lp;
    249 
    250   h->lp_task = NULL;
    251   while (NULL != (lp = GNUNET_CONTAINER_heap_peek (h->lp_heap)))
    252   {
    253     if (GNUNET_TIME_absolute_is_future (lp->timeout))
    254       break;
    255     GNUNET_assert (lp ==
    256                    GNUNET_CONTAINER_heap_remove_root (h->lp_heap));
    257     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    258                 "Timeout reached for long poller %p\n",
    259                 lp->conn);
    260     TALER_FAKEBANK_lp_trigger_ (lp);
    261   }
    262   if (NULL == lp)
    263     return;
    264   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 reschedule_lp_timeout (struct TALER_FAKEBANK_Handle *h,
    278                        struct GNUNET_TIME_Absolute t)
    279 {
    280   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   if (-1 != h->lp_event)
    285 #else
    286   if (-1 != h->lp_event_in && -1 != h->lp_event_out)
    287 #endif
    288   {
    289     uint64_t num = 1;
    290 
    291     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     if (NULL != h->lp_task)
    305       GNUNET_SCHEDULER_cancel (h->lp_task);
    306     h->lp_task = GNUNET_SCHEDULER_add_at (t,
    307                                           &lp_timeout,
    308                                           h);
    309   }
    310 }
    311 
    312 
    313 void
    314 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   lp = GNUNET_new (struct LongPoller);
    326   lp->account = acc;
    327   lp->h = h;
    328   lp->wo = wo;
    329   lp->conn = connection;
    330   lp->timeout = GNUNET_TIME_relative_to_absolute (lp_timeout);
    331   lp->type = dir;
    332   lp->hn = GNUNET_CONTAINER_heap_insert (h->lp_heap,
    333                                          lp,
    334                                          lp->timeout.abs_value_us);
    335   toc = (lp ==
    336          GNUNET_CONTAINER_heap_peek (h->lp_heap));
    337   GNUNET_CONTAINER_DLL_insert (acc->lp_head,
    338                                acc->lp_tail,
    339                                lp);
    340   MHD_suspend_connection (connection);
    341   if (toc)
    342     reschedule_lp_timeout (h,
    343                            lp->timeout);
    344 
    345 }