aboutsummaryrefslogtreecommitdiff
path: root/src/testbed/gnunet-service-testbed_cache.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/testbed/gnunet-service-testbed_cache.c')
-rw-r--r--src/testbed/gnunet-service-testbed_cache.c1001
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 */
41enum 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 */
58struct 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 */
65struct 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 */
104struct 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 */
150struct 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 */
247static 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 */
255static 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 */
263static struct CacheEntry *lru_cache_tail;
264
265/**
266 * the size of the LRU queue
267 */
268static unsigned int lru_cache_size;
269
270/**
271 * the threshold size for the LRU queue
272 */
273static unsigned int lru_cache_threshold_size;
274
275/**
276 * The total number of elements in cache
277 */
278static 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 */
287static struct CacheEntry *
288cache_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 */
306static void
307close_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 */
351static struct CacheEntry *
352add_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 */
379static struct GSTCacheGetHandle *
380search_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 */
413static void
414call_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 */
452static void
453peer_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 */
501static void
502transport_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 */
517static void
518opstart_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 */
548static void
549oprelease_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 */
573static void
574core_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 */
605static void
606core_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 */
621static void
622opstart_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 */
652static void
653oprelease_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 */
675static struct GSTCacheGetHandle *
676cache_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 */
771static int
772cache_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 */
803void
804GST_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 */
817void
818GST_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 */
835void
836GST_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 */
895struct GSTCacheGetHandle *
896GST_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 */
936struct GSTCacheGetHandle *
937GST_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 */
962const struct GNUNET_MessageHeader *
963GST_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 */
986void
987GST_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 */