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 }