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 : }
|