diff options
Diffstat (limited to 'src/testbed/gnunet-service-testbed_cache.c')
-rw-r--r-- | src/testbed/gnunet-service-testbed_cache.c | 1001 |
1 files changed, 1001 insertions, 0 deletions
diff --git a/src/testbed/gnunet-service-testbed_cache.c b/src/testbed/gnunet-service-testbed_cache.c new file mode 100644 index 000000000..f91e8c9d9 --- /dev/null +++ b/src/testbed/gnunet-service-testbed_cache.c | |||
@@ -0,0 +1,1001 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | (C) 2012 Christian Grothoff (and other contributing authors) | ||
4 | |||
5 | GNUnet is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published | ||
7 | by the Free Software Foundation; either version 2, or (at your | ||
8 | option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with GNUnet; see the file COPYING. If not, write to the | ||
17 | Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
18 | Boston, MA 02111-1307, USA. | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file testbed/gnunet-service-testbed_cache.h | ||
23 | * @brief testbed cache implementation | ||
24 | * @author Sree Harsha Totakura | ||
25 | */ | ||
26 | #include "gnunet-service-testbed.h" | ||
27 | |||
28 | /** | ||
29 | * Redefine LOG with a changed log component string | ||
30 | */ | ||
31 | #ifdef LOG | ||
32 | #undef LOG | ||
33 | #endif | ||
34 | #define LOG(kind,...) \ | ||
35 | GNUNET_log_from (kind, "testbed-cache", __VA_ARGS__) | ||
36 | |||
37 | |||
38 | /** | ||
39 | * Type of cache-get requests | ||
40 | */ | ||
41 | enum CacheGetType | ||
42 | { | ||
43 | /** | ||
44 | * Get transport handle | ||
45 | */ | ||
46 | CGT_TRANSPORT_HANDLE = 1, | ||
47 | |||
48 | /** | ||
49 | * Get core handle | ||
50 | */ | ||
51 | CGT_CORE_HANDLE | ||
52 | }; | ||
53 | |||
54 | |||
55 | /** | ||
56 | * The cache-get request handle | ||
57 | */ | ||
58 | struct GSTCacheGetHandle; | ||
59 | |||
60 | |||
61 | /** | ||
62 | * This context structure is used to maintain a queue of notifications to check | ||
63 | * which of them are to be notified when a peer is connected. | ||
64 | */ | ||
65 | struct ConnectNotifyContext | ||
66 | { | ||
67 | /** | ||
68 | * The next ptr for the DLL | ||
69 | */ | ||
70 | struct ConnectNotifyContext *next; | ||
71 | |||
72 | /** | ||
73 | * The prev ptr for the DLL | ||
74 | */ | ||
75 | struct ConnectNotifyContext *prev; | ||
76 | |||
77 | /** | ||
78 | * The peer identity of the target peer. When this target peer is connected, | ||
79 | * call the notify callback | ||
80 | */ | ||
81 | const struct GNUNET_PeerIdentity *target; | ||
82 | |||
83 | /** | ||
84 | * The notify callback to be called when the target peer is connected | ||
85 | */ | ||
86 | GST_cache_peer_connect_notify cb; | ||
87 | |||
88 | /** | ||
89 | * The closure for the notify callback | ||
90 | */ | ||
91 | void *cb_cls; | ||
92 | |||
93 | /** | ||
94 | * The GSTCacheGetHandle reposible for creating this context | ||
95 | */ | ||
96 | struct GSTCacheGetHandle *cgh; | ||
97 | |||
98 | }; | ||
99 | |||
100 | |||
101 | /** | ||
102 | * The cache-get request handle | ||
103 | */ | ||
104 | struct GSTCacheGetHandle | ||
105 | { | ||
106 | /** | ||
107 | * The next ptr for the DLL. Used in struct CacheEntry | ||
108 | */ | ||
109 | struct GSTCacheGetHandle *next; | ||
110 | |||
111 | /** | ||
112 | * The prev ptr for the DLL. Used in struct CacheEntry | ||
113 | */ | ||
114 | struct GSTCacheGetHandle *prev; | ||
115 | |||
116 | /** | ||
117 | * The cache entry object this handle corresponds to | ||
118 | */ | ||
119 | struct CacheEntry *entry; | ||
120 | |||
121 | /** | ||
122 | * The cache callback to call when a handle is available | ||
123 | */ | ||
124 | GST_cache_handle_ready_cb cb; | ||
125 | |||
126 | /** | ||
127 | * The closure for the above callback | ||
128 | */ | ||
129 | void *cb_cls; | ||
130 | |||
131 | /** | ||
132 | * The peer connect notify context created for this handle; can be NULL | ||
133 | */ | ||
134 | struct ConnectNotifyContext *nctxt; | ||
135 | |||
136 | /** | ||
137 | * The type of this cache-get request | ||
138 | */ | ||
139 | enum CacheGetType type; | ||
140 | |||
141 | /** | ||
142 | * Did we call the cache callback already? | ||
143 | */ | ||
144 | int notify_called; | ||
145 | }; | ||
146 | |||
147 | /** | ||
148 | * Cache entry | ||
149 | */ | ||
150 | struct CacheEntry | ||
151 | { | ||
152 | /** | ||
153 | * DLL next ptr for least recently used cache entries | ||
154 | */ | ||
155 | struct CacheEntry *next; | ||
156 | |||
157 | /** | ||
158 | * DLL prev ptr for least recently used cache entries | ||
159 | */ | ||
160 | struct CacheEntry *prev; | ||
161 | |||
162 | /** | ||
163 | * The transport handle to the peer corresponding to this entry; can be NULL | ||
164 | */ | ||
165 | struct GNUNET_TRANSPORT_Handle *transport_handle_; | ||
166 | |||
167 | /** | ||
168 | * The operation handle for transport handle | ||
169 | */ | ||
170 | struct GNUNET_TESTBED_Operation *transport_op_; | ||
171 | |||
172 | /** | ||
173 | * The core handle to the peer corresponding to this entry; can be NULL | ||
174 | */ | ||
175 | struct GNUNET_CORE_Handle *core_handle; | ||
176 | |||
177 | /** | ||
178 | * The operation handle for core handle | ||
179 | */ | ||
180 | struct GNUNET_TESTBED_Operation *core_op; | ||
181 | |||
182 | /** | ||
183 | * The peer identity of this peer. Will be set upon opening a connection to | ||
184 | * the peers CORE service. Will be NULL until then and after the CORE | ||
185 | * connection is closed | ||
186 | */ | ||
187 | struct GNUNET_PeerIdentity *peer_identity; | ||
188 | |||
189 | /** | ||
190 | * The configuration of the peer. Should be not NULL as long as the core_handle | ||
191 | * or transport_handle are valid | ||
192 | */ | ||
193 | struct GNUNET_CONFIGURATION_Handle *cfg; | ||
194 | |||
195 | /** | ||
196 | * The key for this entry | ||
197 | */ | ||
198 | struct GNUNET_HashCode key; | ||
199 | |||
200 | /** | ||
201 | * The HELLO message | ||
202 | */ | ||
203 | struct GNUNET_MessageHeader *hello; | ||
204 | |||
205 | /** | ||
206 | * the head of the CacheGetHandle queue | ||
207 | */ | ||
208 | struct GSTCacheGetHandle *cgh_qhead; | ||
209 | |||
210 | /** | ||
211 | * the tail of the CacheGetHandle queue | ||
212 | */ | ||
213 | struct GSTCacheGetHandle *cgh_qtail; | ||
214 | |||
215 | /** | ||
216 | * DLL head for the queue of notifications contexts to check which of them are to | ||
217 | * be notified when a peer is connected. | ||
218 | */ | ||
219 | struct ConnectNotifyContext *nctxt_qhead; | ||
220 | |||
221 | /** | ||
222 | * DLL tail for the queue of notifications contexts to check which of them are to | ||
223 | * be notified when a peer is connected. | ||
224 | */ | ||
225 | struct ConnectNotifyContext *nctxt_qtail; | ||
226 | |||
227 | /** | ||
228 | * The task that calls the cache callback | ||
229 | */ | ||
230 | GNUNET_SCHEDULER_TaskIdentifier notify_task; | ||
231 | |||
232 | /** | ||
233 | * Number of operations this cache entry is being used | ||
234 | */ | ||
235 | unsigned int demand; | ||
236 | |||
237 | /** | ||
238 | * The id of the peer this entry corresponds to | ||
239 | */ | ||
240 | unsigned int peer_id; | ||
241 | }; | ||
242 | |||
243 | |||
244 | /** | ||
245 | * Hashmap to maintain cache | ||
246 | */ | ||
247 | static struct GNUNET_CONTAINER_MultiHashMap *cache; | ||
248 | |||
249 | /** | ||
250 | * DLL head for least recently used cache entries; least recently used | ||
251 | * cache items are at the head. The cache enties are added to this queue when | ||
252 | * their demand becomes zero. They are removed from the queue when they are | ||
253 | * needed by any operation. | ||
254 | */ | ||
255 | static struct CacheEntry *lru_cache_head; | ||
256 | |||
257 | /** | ||
258 | * DLL tail for least recently used cache entries; recently used cache | ||
259 | * items are at the tail.The cache enties are added to this queue when | ||
260 | * their demand becomes zero. They are removed from the queue when they are | ||
261 | * needed by any operation. | ||
262 | */ | ||
263 | static struct CacheEntry *lru_cache_tail; | ||
264 | |||
265 | /** | ||
266 | * the size of the LRU queue | ||
267 | */ | ||
268 | static unsigned int lru_cache_size; | ||
269 | |||
270 | /** | ||
271 | * the threshold size for the LRU queue | ||
272 | */ | ||
273 | static unsigned int lru_cache_threshold_size; | ||
274 | |||
275 | /** | ||
276 | * The total number of elements in cache | ||
277 | */ | ||
278 | static unsigned int cache_size; | ||
279 | |||
280 | |||
281 | /** | ||
282 | * Looks up in the cache and returns the entry | ||
283 | * | ||
284 | * @param id the peer identity of the peer whose corresponding entry has to be looked up | ||
285 | * @return the HELLO message; NULL if not found | ||
286 | */ | ||
287 | static struct CacheEntry * | ||
288 | cache_lookup (const struct GNUNET_HashCode *key) | ||
289 | { | ||
290 | struct CacheEntry *entry; | ||
291 | |||
292 | if (NULL == cache) | ||
293 | return NULL; | ||
294 | entry = GNUNET_CONTAINER_multihashmap_get (cache, key); | ||
295 | return entry; | ||
296 | } | ||
297 | |||
298 | |||
299 | /** | ||
300 | * Function to disconnect the core and transport handles; free the existing | ||
301 | * configuration; and remove from the LRU cache list. The entry is left to be in | ||
302 | * the hash table so that the HELLO can still be found later | ||
303 | * | ||
304 | * @param entry the cache entry | ||
305 | */ | ||
306 | static void | ||
307 | close_handles (struct CacheEntry *entry) | ||
308 | { | ||
309 | struct ConnectNotifyContext *ctxt; | ||
310 | |||
311 | GNUNET_assert (0 == entry->demand); | ||
312 | if ((NULL != entry->next) || (NULL != entry->prev)) | ||
313 | { | ||
314 | GNUNET_assert (0 < lru_cache_size); | ||
315 | GNUNET_CONTAINER_DLL_remove (lru_cache_head, lru_cache_tail, entry); | ||
316 | lru_cache_size--; | ||
317 | } | ||
318 | while (NULL != (ctxt = entry->nctxt_qhead)) | ||
319 | { | ||
320 | GNUNET_CONTAINER_DLL_remove (entry->nctxt_qhead, entry->nctxt_qtail, ctxt); | ||
321 | GNUNET_free (ctxt); | ||
322 | } | ||
323 | LOG_DEBUG ("Cleaning up handles from an entry in cache\n"); | ||
324 | if (NULL != entry->transport_handle_) | ||
325 | { | ||
326 | GNUNET_assert (NULL != entry->transport_op_); | ||
327 | GNUNET_TESTBED_operation_done (entry->transport_op_); | ||
328 | entry->transport_op_ = NULL; | ||
329 | } | ||
330 | if (NULL != entry->core_handle) | ||
331 | { | ||
332 | GNUNET_assert (NULL != entry->core_op); | ||
333 | GNUNET_TESTBED_operation_done (entry->core_op); | ||
334 | entry->core_op = NULL; | ||
335 | } | ||
336 | if (NULL != entry->cfg) | ||
337 | { | ||
338 | GNUNET_CONFIGURATION_destroy (entry->cfg); | ||
339 | entry->cfg = NULL; | ||
340 | } | ||
341 | } | ||
342 | |||
343 | |||
344 | /** | ||
345 | * Creates a new cache entry and then puts it into the cache's hashtable. | ||
346 | * | ||
347 | * @param key the hash code to use for inserting the newly created entry | ||
348 | * @param peer_id the index of the peer to tag the newly created entry | ||
349 | * @return the newly created entry | ||
350 | */ | ||
351 | static struct CacheEntry * | ||
352 | add_entry (const struct GNUNET_HashCode *key, unsigned int peer_id) | ||
353 | { | ||
354 | struct CacheEntry *entry; | ||
355 | |||
356 | entry = GNUNET_malloc (sizeof (struct CacheEntry)); | ||
357 | entry->peer_id = peer_id; | ||
358 | memcpy (&entry->key, key, sizeof (struct GNUNET_HashCode)); | ||
359 | GNUNET_assert (GNUNET_OK == | ||
360 | GNUNET_CONTAINER_multihashmap_put (cache, &entry->key, | ||
361 | entry, | ||
362 | GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)); | ||
363 | cache_size++; | ||
364 | return entry; | ||
365 | } | ||
366 | |||
367 | |||
368 | /** | ||
369 | * Function to find a suitable GSTCacheGetHandle which is waiting for one of the | ||
370 | * handles in given entry to be available. | ||
371 | * | ||
372 | * @param entry the cache entry whose GSTCacheGetHandle list has to be searched | ||
373 | * @param head the starting list element in the GSTCacheGetHandle where the | ||
374 | * search has to be begin | ||
375 | * @return a suitable GSTCacheGetHandle whose handle ready notify callback | ||
376 | * hasn't been called yet. NULL if no such suitable GSTCacheGetHandle | ||
377 | * is found | ||
378 | */ | ||
379 | static struct GSTCacheGetHandle * | ||
380 | search_suitable_cgh (const struct CacheEntry *entry, | ||
381 | const struct GSTCacheGetHandle *head) | ||
382 | { | ||
383 | const struct GSTCacheGetHandle *cgh; | ||
384 | |||
385 | for (cgh=head; NULL != cgh; cgh=cgh->next) | ||
386 | { | ||
387 | if (GNUNET_YES == cgh->notify_called) | ||
388 | return NULL; | ||
389 | switch (cgh->type) | ||
390 | { | ||
391 | case CGT_TRANSPORT_HANDLE: | ||
392 | if (NULL == entry->transport_handle_) | ||
393 | continue; | ||
394 | break; | ||
395 | case CGT_CORE_HANDLE: | ||
396 | if (NULL == entry->core_handle) | ||
397 | continue; | ||
398 | break; | ||
399 | } | ||
400 | break; | ||
401 | } | ||
402 | return (struct GSTCacheGetHandle *) cgh; | ||
403 | } | ||
404 | |||
405 | |||
406 | /** | ||
407 | * Task to call the handle ready notify callback of a queued GSTCacheGetHandle | ||
408 | * of an entry when one or all of its handles are available. | ||
409 | * | ||
410 | * @param cls the cache entry | ||
411 | * @param tc the task context from scheduler | ||
412 | */ | ||
413 | static void | ||
414 | call_cgh_cb (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
415 | { | ||
416 | struct CacheEntry *entry = cls; | ||
417 | struct GSTCacheGetHandle *cgh; | ||
418 | const struct GSTCacheGetHandle *cgh2; | ||
419 | |||
420 | GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != entry->notify_task); | ||
421 | entry->notify_task = GNUNET_SCHEDULER_NO_TASK; | ||
422 | cgh = search_suitable_cgh (entry, entry->cgh_qhead); | ||
423 | GNUNET_assert (NULL != cgh); | ||
424 | cgh2 = NULL; | ||
425 | if (NULL != cgh->next) | ||
426 | cgh2 = search_suitable_cgh (entry, cgh->next); | ||
427 | GNUNET_CONTAINER_DLL_remove (entry->cgh_qhead, entry->cgh_qtail, cgh); | ||
428 | cgh->notify_called = GNUNET_YES; | ||
429 | GNUNET_CONTAINER_DLL_insert_tail (entry->cgh_qhead, entry->cgh_qtail, cgh); | ||
430 | if (NULL != cgh2) | ||
431 | entry->notify_task = GNUNET_SCHEDULER_add_now (&call_cgh_cb, entry); | ||
432 | if (NULL != cgh->nctxt) | ||
433 | {/* Register the peer connect notify callback */ | ||
434 | GNUNET_CONTAINER_DLL_insert_tail (entry->nctxt_qhead, entry->nctxt_qtail, | ||
435 | cgh->nctxt); | ||
436 | } | ||
437 | LOG_DEBUG ("Calling notify for handle type %u\n", cgh->type); | ||
438 | cgh->cb (cgh->cb_cls, entry->core_handle, | ||
439 | entry->transport_handle_, entry->peer_identity); | ||
440 | } | ||
441 | |||
442 | |||
443 | /** | ||
444 | * Function called from peer connect notify callbacks from CORE and TRANSPORT | ||
445 | * connections. This function calls the pendning peer connect notify callbacks | ||
446 | * which are queued in an entry. | ||
447 | * | ||
448 | * @param cls the cache entry | ||
449 | * @param peer the peer that connected | ||
450 | * @param type the type of the handle this notification corresponds to | ||
451 | */ | ||
452 | static void | ||
453 | peer_connect_notify_cb (void *cls, | ||
454 | const struct GNUNET_PeerIdentity *peer, | ||
455 | const enum CacheGetType type) | ||
456 | { | ||
457 | struct CacheEntry *entry = cls; | ||
458 | struct ConnectNotifyContext *ctxt; | ||
459 | struct ConnectNotifyContext *ctxt2; | ||
460 | GST_cache_peer_connect_notify cb; | ||
461 | void *cb_cls; | ||
462 | |||
463 | |||
464 | for (ctxt=entry->nctxt_qhead; NULL != ctxt;) | ||
465 | { | ||
466 | GNUNET_assert (NULL != ctxt->cgh); | ||
467 | if (type != ctxt->cgh->type) | ||
468 | { | ||
469 | ctxt = ctxt->next; | ||
470 | continue; | ||
471 | } | ||
472 | if (0 != memcmp (ctxt->target, peer, sizeof (struct GNUNET_PeerIdentity))) | ||
473 | { | ||
474 | ctxt = ctxt->next; | ||
475 | continue; | ||
476 | } | ||
477 | cb = ctxt->cb; | ||
478 | cb_cls = ctxt->cb_cls; | ||
479 | ctxt->cgh->nctxt = NULL; | ||
480 | ctxt2 = ctxt->next; | ||
481 | GNUNET_CONTAINER_DLL_remove (entry->nctxt_qhead, entry->nctxt_qtail, ctxt); | ||
482 | GNUNET_free (ctxt); | ||
483 | ctxt = ctxt2; | ||
484 | cb (cb_cls, peer); | ||
485 | } | ||
486 | if (NULL == ctxt) | ||
487 | return; | ||
488 | |||
489 | } | ||
490 | |||
491 | |||
492 | /** | ||
493 | * Function called to notify transport users that another | ||
494 | * peer connected to us. | ||
495 | * | ||
496 | * @param cls closure | ||
497 | * @param peer the peer that connected | ||
498 | * @param ats performance data | ||
499 | * @param ats_count number of entries in ats (excluding 0-termination) | ||
500 | */ | ||
501 | static void | ||
502 | transport_peer_connect_notify_cb (void *cls, | ||
503 | const struct GNUNET_PeerIdentity *peer, | ||
504 | const struct GNUNET_ATS_Information *ats, | ||
505 | uint32_t ats_count) | ||
506 | { | ||
507 | peer_connect_notify_cb (cls, peer, CGT_TRANSPORT_HANDLE); | ||
508 | } | ||
509 | |||
510 | |||
511 | /** | ||
512 | * Function called when resources for opening a connection to TRANSPORT are | ||
513 | * available. | ||
514 | * | ||
515 | * @param cls the cache entry | ||
516 | */ | ||
517 | static void | ||
518 | opstart_get_handle_transport (void *cls) | ||
519 | { | ||
520 | struct CacheEntry *entry = cls; | ||
521 | |||
522 | GNUNET_assert (NULL != entry); | ||
523 | LOG_DEBUG ("Opening a transport connection to peer %u\n", entry->peer_id); | ||
524 | entry->transport_handle_ = | ||
525 | GNUNET_TRANSPORT_connect (entry->cfg, | ||
526 | NULL, entry, | ||
527 | NULL, | ||
528 | &transport_peer_connect_notify_cb, NULL); | ||
529 | if (NULL == entry->transport_handle_) | ||
530 | { | ||
531 | GNUNET_break (0); | ||
532 | return; | ||
533 | } | ||
534 | //GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == entry->notify_task); | ||
535 | if (0 == entry->demand) | ||
536 | return; | ||
537 | if (GNUNET_NO == entry->cgh_qhead->notify_called) | ||
538 | entry->notify_task = GNUNET_SCHEDULER_add_now (&call_cgh_cb, entry); | ||
539 | } | ||
540 | |||
541 | |||
542 | /** | ||
543 | * Function called when the operation responsible for opening a TRANSPORT | ||
544 | * connection is marked as done. | ||
545 | * | ||
546 | * @param cls the cache entry | ||
547 | */ | ||
548 | static void | ||
549 | oprelease_get_handle_transport (void *cls) | ||
550 | { | ||
551 | struct CacheEntry *entry = cls; | ||
552 | |||
553 | if (NULL == entry->transport_handle_) | ||
554 | return; | ||
555 | GNUNET_TRANSPORT_disconnect (entry->transport_handle_); | ||
556 | entry->transport_handle_ = NULL; | ||
557 | } | ||
558 | |||
559 | |||
560 | /** | ||
561 | * Function called after GNUNET_CORE_connect has succeeded (or failed | ||
562 | * for good). Note that the private key of the peer is intentionally | ||
563 | * not exposed here; if you need it, your process should try to read | ||
564 | * the private key file directly (which should work if you are | ||
565 | * authorized...). Implementations of this function must not call | ||
566 | * GNUNET_CORE_disconnect (other than by scheduling a new task to | ||
567 | * do this later). | ||
568 | * | ||
569 | * @param cls closure | ||
570 | * @param server handle to the server, NULL if we failed | ||
571 | * @param my_identity ID of this peer, NULL if we failed | ||
572 | */ | ||
573 | static void | ||
574 | core_startup_cb (void *cls, | ||
575 | struct GNUNET_CORE_Handle * server, | ||
576 | const struct GNUNET_PeerIdentity *my_identity) | ||
577 | { | ||
578 | struct CacheEntry *entry = cls; | ||
579 | |||
580 | if (NULL == my_identity) | ||
581 | { | ||
582 | GNUNET_break (0); | ||
583 | return; | ||
584 | } | ||
585 | GNUNET_assert (NULL == entry->peer_identity); | ||
586 | entry->core_handle = server; | ||
587 | entry->peer_identity = GNUNET_malloc (sizeof (struct GNUNET_PeerIdentity)); | ||
588 | memcpy (entry->peer_identity, my_identity, | ||
589 | sizeof (struct GNUNET_PeerIdentity)); | ||
590 | if (0 == entry->demand) | ||
591 | return; | ||
592 | if (GNUNET_NO == entry->cgh_qhead->notify_called) | ||
593 | entry->notify_task = GNUNET_SCHEDULER_add_now (&call_cgh_cb, entry); | ||
594 | } | ||
595 | |||
596 | |||
597 | /** | ||
598 | * Method called whenever a given peer connects at CORE level | ||
599 | * | ||
600 | * @param cls closure | ||
601 | * @param peer peer identity this notification is about | ||
602 | * @param atsi performance data for the connection | ||
603 | * @param atsi_count number of records in 'atsi' | ||
604 | */ | ||
605 | static void | ||
606 | core_peer_connect_cb (void *cls, | ||
607 | const struct GNUNET_PeerIdentity * peer, | ||
608 | const struct GNUNET_ATS_Information * atsi, | ||
609 | unsigned int atsi_count) | ||
610 | { | ||
611 | peer_connect_notify_cb (cls, peer, CGT_CORE_HANDLE); | ||
612 | } | ||
613 | |||
614 | |||
615 | /** | ||
616 | * Function called when resources for opening a connection to CORE are | ||
617 | * available. | ||
618 | * | ||
619 | * @param cls the cache entry | ||
620 | */ | ||
621 | static void | ||
622 | opstart_get_handle_core (void *cls) | ||
623 | { | ||
624 | struct CacheEntry *entry = cls; | ||
625 | const struct GNUNET_CORE_MessageHandler no_handlers[] = { | ||
626 | {NULL, 0, 0} | ||
627 | }; | ||
628 | |||
629 | GNUNET_assert (NULL != entry); | ||
630 | LOG_DEBUG ("Opening a CORE connection to peer %u\n", entry->peer_id); | ||
631 | /* void?: We also get the handle when the connection to CORE is successful */ | ||
632 | (void) GNUNET_CORE_connect (entry->cfg, | ||
633 | entry, | ||
634 | &core_startup_cb, | ||
635 | &core_peer_connect_cb, | ||
636 | NULL, /* disconnect cb */ | ||
637 | NULL, /* inbound notify */ | ||
638 | GNUNET_NO, | ||
639 | NULL, /* outbound notify */ | ||
640 | GNUNET_NO, | ||
641 | no_handlers); | ||
642 | //GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == entry->notify_task); | ||
643 | } | ||
644 | |||
645 | |||
646 | /** | ||
647 | * Function called when the operation responsible for opening a TRANSPORT | ||
648 | * connection is marked as done. | ||
649 | * | ||
650 | * @param cls the cache entry | ||
651 | */ | ||
652 | static void | ||
653 | oprelease_get_handle_core (void *cls) | ||
654 | { | ||
655 | struct CacheEntry *entry = cls; | ||
656 | |||
657 | if (NULL == entry->core_handle) | ||
658 | return; | ||
659 | GNUNET_CORE_disconnect (entry->core_handle); | ||
660 | entry->core_handle = NULL; | ||
661 | GNUNET_free_non_null (entry->peer_identity); | ||
662 | entry->peer_identity = NULL; | ||
663 | } | ||
664 | |||
665 | |||
666 | /** | ||
667 | * Function to get a handle with given configuration. The type of the handle is | ||
668 | * implicitly provided in the GSTCacheGetHandle. If the handle is already cached | ||
669 | * before, it will be retured in the given callback; the peer_id is used to | ||
670 | * lookup in the cache; if not, a new operation is started to open the transport | ||
671 | * handle and will be given in the callback when it is available. | ||
672 | * | ||
673 | * @param cls the cache entry | ||
674 | */ | ||
675 | static struct GSTCacheGetHandle * | ||
676 | cache_get_handle (unsigned int peer_id, | ||
677 | struct GSTCacheGetHandle *cgh, | ||
678 | const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
679 | const struct GNUNET_PeerIdentity *target, | ||
680 | GST_cache_peer_connect_notify connect_notify_cb, | ||
681 | void *connect_notify_cb_cls) | ||
682 | { | ||
683 | struct GNUNET_HashCode key; | ||
684 | void *handle; | ||
685 | struct CacheEntry *entry; | ||
686 | struct ConnectNotifyContext *ctxt; | ||
687 | struct GNUNET_TESTBED_Operation *op; | ||
688 | |||
689 | GNUNET_assert (0 != cgh->type); | ||
690 | GNUNET_CRYPTO_hash (&peer_id, sizeof (peer_id), &key); | ||
691 | handle = NULL; | ||
692 | entry = cache_lookup (&key); | ||
693 | if (NULL != entry) | ||
694 | { | ||
695 | if (0 == entry->demand) | ||
696 | { | ||
697 | GNUNET_assert (0 < lru_cache_size); | ||
698 | GNUNET_CONTAINER_DLL_remove (lru_cache_head, lru_cache_tail, entry); | ||
699 | lru_cache_size--; | ||
700 | } | ||
701 | switch (cgh->type) | ||
702 | { | ||
703 | case CGT_TRANSPORT_HANDLE: | ||
704 | handle = entry->transport_handle_; | ||
705 | if (NULL != handle) | ||
706 | LOG_DEBUG ("Found TRANSPORT handle in cache for peer %u\n", entry->peer_id); | ||
707 | break; | ||
708 | case CGT_CORE_HANDLE: | ||
709 | handle = entry->core_handle; | ||
710 | if (NULL != handle) | ||
711 | LOG_DEBUG ("Found CORE handle in cache for peer %u\n", entry->peer_id); | ||
712 | break; | ||
713 | } | ||
714 | } | ||
715 | if (NULL == entry) | ||
716 | entry = add_entry (&key, peer_id); | ||
717 | if (NULL == entry->cfg) | ||
718 | entry->cfg = GNUNET_CONFIGURATION_dup (cfg); | ||
719 | entry->demand++; | ||
720 | cgh->entry = entry; | ||
721 | GNUNET_CONTAINER_DLL_insert (entry->cgh_qhead, entry->cgh_qtail, cgh); | ||
722 | if ((NULL != target) && (NULL != connect_notify_cb)) | ||
723 | { | ||
724 | ctxt = GNUNET_malloc (sizeof (struct ConnectNotifyContext)); | ||
725 | ctxt->target = target; | ||
726 | ctxt->cb = connect_notify_cb; | ||
727 | ctxt->cb_cls = connect_notify_cb_cls; | ||
728 | GNUNET_assert (NULL == cgh->nctxt); | ||
729 | cgh->nctxt = ctxt; | ||
730 | ctxt->cgh = cgh; | ||
731 | } | ||
732 | if (NULL != handle) | ||
733 | { | ||
734 | if (GNUNET_SCHEDULER_NO_TASK == entry->notify_task) | ||
735 | entry->notify_task = GNUNET_SCHEDULER_add_now (&call_cgh_cb, entry); | ||
736 | return cgh; | ||
737 | } | ||
738 | switch (cgh->type) | ||
739 | { | ||
740 | case CGT_TRANSPORT_HANDLE: | ||
741 | if (NULL != entry->transport_op_) | ||
742 | return cgh; | ||
743 | op = GNUNET_TESTBED_operation_create_ (entry, &opstart_get_handle_transport, | ||
744 | &oprelease_get_handle_transport); | ||
745 | entry->transport_op_ = op; | ||
746 | break; | ||
747 | case CGT_CORE_HANDLE: | ||
748 | if (NULL != entry->core_op) | ||
749 | return cgh; | ||
750 | op = GNUNET_TESTBED_operation_create_ (entry, &opstart_get_handle_core, | ||
751 | &oprelease_get_handle_core); | ||
752 | entry->core_op = op; | ||
753 | break; | ||
754 | } | ||
755 | GNUNET_TESTBED_operation_queue_insert_ (GST_opq_openfds, op); | ||
756 | GNUNET_TESTBED_operation_begin_wait_ (op); | ||
757 | return cgh; | ||
758 | } | ||
759 | |||
760 | |||
761 | /** | ||
762 | * Iterator over hash map entries. | ||
763 | * | ||
764 | * @param cls closure | ||
765 | * @param key current key code | ||
766 | * @param value value in the hash map | ||
767 | * @return GNUNET_YES if we should continue to | ||
768 | * iterate, | ||
769 | * GNUNET_NO if not. | ||
770 | */ | ||
771 | static int | ||
772 | cache_clear_iterator (void *cls, | ||
773 | const struct GNUNET_HashCode * key, | ||
774 | void *value) | ||
775 | { | ||
776 | struct CacheEntry *entry = value; | ||
777 | static unsigned int ncleared; | ||
778 | |||
779 | GNUNET_assert (NULL != entry); | ||
780 | GNUNET_break (0 == entry->demand); | ||
781 | LOG_DEBUG ("Clearing entry %u of %u\n", ++ncleared, cache_size); | ||
782 | GNUNET_CONTAINER_multihashmap_remove (cache, key, value); | ||
783 | if (0 == entry->demand) | ||
784 | close_handles (entry); | ||
785 | GNUNET_free_non_null (entry->hello); | ||
786 | GNUNET_break (NULL == entry->transport_handle_); | ||
787 | GNUNET_break (NULL == entry->transport_op_); | ||
788 | GNUNET_break (NULL == entry->core_handle); | ||
789 | GNUNET_break (NULL == entry->core_op); | ||
790 | GNUNET_break (NULL == entry->cfg); | ||
791 | GNUNET_assert (NULL == entry->cgh_qhead); | ||
792 | GNUNET_assert (NULL == entry->cgh_qtail); | ||
793 | GNUNET_assert (NULL == entry->nctxt_qhead); | ||
794 | GNUNET_assert (NULL == entry->nctxt_qtail); | ||
795 | GNUNET_free (entry); | ||
796 | return GNUNET_YES; | ||
797 | } | ||
798 | |||
799 | |||
800 | /** | ||
801 | * Clear cache | ||
802 | */ | ||
803 | void | ||
804 | GST_cache_clear () | ||
805 | { | ||
806 | GNUNET_CONTAINER_multihashmap_iterate (cache, &cache_clear_iterator, NULL); | ||
807 | GNUNET_assert (0 == GNUNET_CONTAINER_multihashmap_size (cache)); | ||
808 | GNUNET_CONTAINER_multihashmap_destroy (cache); | ||
809 | } | ||
810 | |||
811 | |||
812 | /** | ||
813 | * Initializes the cache | ||
814 | * | ||
815 | * @param size the size of the cache | ||
816 | */ | ||
817 | void | ||
818 | GST_cache_init (unsigned int size) | ||
819 | { | ||
820 | if (0 == size) | ||
821 | return; | ||
822 | lru_cache_threshold_size = size; | ||
823 | if (size > 1) | ||
824 | size = size / 2; | ||
825 | cache = GNUNET_CONTAINER_multihashmap_create (size, GNUNET_YES); | ||
826 | } | ||
827 | |||
828 | |||
829 | /** | ||
830 | * Mark the GetCacheHandle as being done if a handle has been provided already | ||
831 | * or as being cancelled if the callback for the handle hasn't been called. | ||
832 | * | ||
833 | * @param cgh the CacheGetHandle handle | ||
834 | */ | ||
835 | void | ||
836 | GST_cache_get_handle_done (struct GSTCacheGetHandle *cgh) | ||
837 | { | ||
838 | struct CacheEntry *entry; | ||
839 | |||
840 | entry = cgh->entry; | ||
841 | GNUNET_assert (NULL != entry); | ||
842 | GNUNET_assert (0 < entry->demand); | ||
843 | entry->demand--; | ||
844 | if (GNUNET_SCHEDULER_NO_TASK != entry->notify_task) | ||
845 | { | ||
846 | GNUNET_SCHEDULER_cancel (entry->notify_task); | ||
847 | entry->notify_task = GNUNET_SCHEDULER_NO_TASK; | ||
848 | } | ||
849 | GNUNET_CONTAINER_DLL_remove (entry->cgh_qhead, entry->cgh_qtail, cgh); | ||
850 | if (NULL != cgh->nctxt) | ||
851 | { | ||
852 | GNUNET_assert (cgh == cgh->nctxt->cgh); | ||
853 | if (GNUNET_YES == cgh->notify_called) | ||
854 | GNUNET_CONTAINER_DLL_remove (entry->nctxt_qhead, entry->nctxt_qtail, cgh->nctxt); | ||
855 | GNUNET_free (cgh->nctxt); | ||
856 | } | ||
857 | GNUNET_free (cgh); | ||
858 | if (0 == entry->demand) | ||
859 | { | ||
860 | GNUNET_CONTAINER_DLL_insert_tail (lru_cache_head, lru_cache_tail, entry); | ||
861 | lru_cache_size++; | ||
862 | if (lru_cache_size > lru_cache_threshold_size) | ||
863 | close_handles (lru_cache_head); | ||
864 | } | ||
865 | else | ||
866 | { | ||
867 | struct GSTCacheGetHandle *cgh2; | ||
868 | |||
869 | if (NULL != (cgh2 = search_suitable_cgh (entry, entry->cgh_qhead))) | ||
870 | entry->notify_task = GNUNET_SCHEDULER_add_now (&call_cgh_cb, entry); | ||
871 | } | ||
872 | } | ||
873 | |||
874 | |||
875 | /** | ||
876 | * Get a transport handle with the given configuration. If the handle is | ||
877 | * already cached before, it will be retured in the given callback; the peer_id | ||
878 | * is used to lookup in the cache; if not, a new operation is started to open the | ||
879 | * transport handle and will be given in the callback when it is available. | ||
880 | * | ||
881 | * @param peer_id the index of the peer | ||
882 | * @param cfg the configuration with which the transport handle has to be | ||
883 | * created if it was not present in the cache | ||
884 | * @param cb the callback to notify when the transport handle is available | ||
885 | * @param cb_cls the closure for the above callback | ||
886 | * @param target the peer identify of the peer whose connection to our TRANSPORT | ||
887 | * subsystem will be notified through the connect_notify_cb. Can be NULL | ||
888 | * @param connect_notify_cb the callback to call when the given target peer is | ||
889 | * connected. This callback will only be called once or never again (in | ||
890 | * case the target peer cannot be connected). Can be NULL | ||
891 | * @param connect_notify_cb_cls the closure for the above callback | ||
892 | * @return the handle which can be used cancel or mark that the handle is no | ||
893 | * longer being used | ||
894 | */ | ||
895 | struct GSTCacheGetHandle * | ||
896 | GST_cache_get_handle_transport (unsigned int peer_id, | ||
897 | const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
898 | GST_cache_handle_ready_cb cb, | ||
899 | void *cb_cls, | ||
900 | const struct GNUNET_PeerIdentity *target, | ||
901 | GST_cache_peer_connect_notify connect_notify_cb, | ||
902 | void *connect_notify_cb_cls) | ||
903 | { | ||
904 | struct GSTCacheGetHandle *cgh; | ||
905 | |||
906 | cgh = GNUNET_malloc (sizeof (struct GSTCacheGetHandle)); | ||
907 | cgh->cb = cb; | ||
908 | cgh->cb_cls = cb_cls; | ||
909 | cgh->type = CGT_TRANSPORT_HANDLE; | ||
910 | return cache_get_handle (peer_id, cgh, cfg, | ||
911 | target, connect_notify_cb, connect_notify_cb_cls); | ||
912 | } | ||
913 | |||
914 | |||
915 | /** | ||
916 | * Get a CORE handle with the given configuration. If the handle is already | ||
917 | * cached before, it will be retured in the given callback; the peer_id is used | ||
918 | * to lookup in the cache. If the handle is not cached before, a new operation | ||
919 | * is started to open the CORE handle and will be given in the callback when it | ||
920 | * is available along with the peer identity | ||
921 | * | ||
922 | * @param peer_id the index of the peer | ||
923 | * @param cfg the configuration with which the transport handle has to be | ||
924 | * created if it was not present in the cache | ||
925 | * @param cb the callback to notify when the transport handle is available | ||
926 | * @param cb_cls the closure for the above callback | ||
927 | * @param target the peer identify of the peer whose connection to our CORE | ||
928 | * subsystem will be notified through the connect_notify_cb. Can be NULL | ||
929 | * @param connect_notify_cb the callback to call when the given target peer is | ||
930 | * connected. This callback will only be called once or never again (in | ||
931 | * case the target peer cannot be connected). Can be NULL | ||
932 | * @param connect_notify_cb_cls the closure for the above callback | ||
933 | * @return the handle which can be used cancel or mark that the handle is no | ||
934 | * longer being used | ||
935 | */ | ||
936 | struct GSTCacheGetHandle * | ||
937 | GST_cache_get_handle_core (unsigned int peer_id, | ||
938 | const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
939 | GST_cache_handle_ready_cb cb, | ||
940 | void *cb_cls, | ||
941 | const struct GNUNET_PeerIdentity *target, | ||
942 | GST_cache_peer_connect_notify connect_notify_cb, | ||
943 | void *connect_notify_cb_cls) | ||
944 | { | ||
945 | struct GSTCacheGetHandle *cgh; | ||
946 | |||
947 | cgh = GNUNET_malloc (sizeof (struct GSTCacheGetHandle)); | ||
948 | cgh->cb = cb; | ||
949 | cgh->cb_cls = cb_cls; | ||
950 | cgh->type = CGT_CORE_HANDLE; | ||
951 | return cache_get_handle (peer_id, cgh, cfg, | ||
952 | target, connect_notify_cb, connect_notify_cb_cls); | ||
953 | } | ||
954 | |||
955 | |||
956 | /** | ||
957 | * Looks up in the hello cache and returns the HELLO of the given peer | ||
958 | * | ||
959 | * @param peer_id the index of the peer whose HELLO has to be looked up | ||
960 | * @return the HELLO message; NULL if not found | ||
961 | */ | ||
962 | const struct GNUNET_MessageHeader * | ||
963 | GST_cache_lookup_hello (const unsigned int peer_id) | ||
964 | { | ||
965 | struct CacheEntry *entry; | ||
966 | struct GNUNET_HashCode key; | ||
967 | |||
968 | LOG_DEBUG ("Looking up HELLO for peer %u\n", peer_id); | ||
969 | GNUNET_CRYPTO_hash (&peer_id, sizeof (peer_id), &key); | ||
970 | entry = cache_lookup (&key); | ||
971 | if (NULL == entry) | ||
972 | return NULL; | ||
973 | if (NULL != entry->hello) | ||
974 | LOG_DEBUG ("HELLO found for peer %u\n", peer_id); | ||
975 | return entry->hello; | ||
976 | } | ||
977 | |||
978 | |||
979 | /** | ||
980 | * Caches the HELLO of the given peer. Updates the HELLO if it was already | ||
981 | * cached before | ||
982 | * | ||
983 | * @param id the peer identity of the peer whose HELLO has to be cached | ||
984 | * @param hello the HELLO message | ||
985 | */ | ||
986 | void | ||
987 | GST_cache_add_hello (const unsigned int peer_id, | ||
988 | const struct GNUNET_MessageHeader *hello) | ||
989 | { | ||
990 | struct CacheEntry *entry; | ||
991 | struct GNUNET_HashCode key; | ||
992 | |||
993 | GNUNET_CRYPTO_hash (&peer_id, sizeof (peer_id), &key); | ||
994 | entry = GNUNET_CONTAINER_multihashmap_get (cache, &key); | ||
995 | if (NULL == entry) | ||
996 | entry = add_entry (&key, peer_id); | ||
997 | GNUNET_free_non_null (entry->hello); | ||
998 | entry->hello = GNUNET_copy_message (hello); | ||
999 | } | ||
1000 | |||
1001 | /* end of gnunet-service-testbed_hc.c */ | ||