ea3cbad3786a7acef7023d66c09aff4fb7a95282
[oweals/gnunet.git] / src / testbed / gnunet-service-testbed_hc.c
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_hc.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_callback 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 static struct CacheEntry *
300 cache_lookup_handles (const struct GNUNET_HashCode *pid,
301                       struct GNUNET_TRANSPORT_Handle **th,
302                       struct GNUNET_CORE_Handle **ch)
303 {
304   struct CacheEntry *entry;
305   
306   GNUNET_assert ((NULL != th) || (NULL != ch));
307   entry = cache_lookup (pid);  
308   if (NULL == entry)
309     return NULL;
310   if ((NULL != entry->transport_handle_) && (NULL != th))
311     *th = entry->transport_handle_;
312   if ((NULL != entry->core_handle) && (NULL != ch))
313     *ch = entry->core_handle;
314   return entry;
315 }
316
317
318 static void
319 cache_remove (struct CacheEntry *entry)
320 {
321   struct ConnectNotifyContext *ctxt;
322   
323   /* We keep the entry in the hash table so that the HELLO can still be found
324      in cache; we will however disconnect the core and transport handles */
325   GNUNET_assert (0 == entry->demand);
326   if ((NULL != entry->next) || (NULL != entry->prev))
327     GNUNET_CONTAINER_DLL_remove (lru_cache_head, lru_cache_tail, entry);
328   while (NULL != (ctxt = entry->nctxt_qhead))
329   {
330     GNUNET_CONTAINER_DLL_remove (entry->nctxt_qhead, entry->nctxt_qtail, ctxt);
331     GNUNET_free (ctxt);
332   }
333   LOG_DEBUG ("Cleaning up handles from an entry in cache\n");
334   if (NULL != entry->transport_handle_)
335   {
336     GNUNET_assert (NULL != entry->transport_op_);
337     GNUNET_TESTBED_operation_done (entry->transport_op_);
338     entry->transport_op_ = NULL;
339   }
340   if (NULL != entry->core_handle)
341   {
342     GNUNET_assert (NULL != entry->core_op);
343     GNUNET_TESTBED_operation_done (entry->core_op);
344     entry->core_op = NULL;
345   }
346   if (NULL != entry->cfg)
347   {
348     GNUNET_CONFIGURATION_destroy (entry->cfg);
349     entry->cfg = NULL;
350   }
351 }
352
353
354 static struct CacheEntry *
355 add_entry (const struct GNUNET_HashCode *key, unsigned int peer_id)
356 {
357   struct CacheEntry *entry;
358
359   entry = GNUNET_malloc (sizeof (struct CacheEntry));
360   entry->peer_id = peer_id;
361   memcpy (&entry->key, key, sizeof (struct GNUNET_HashCode));
362   GNUNET_assert (GNUNET_OK ==
363                  GNUNET_CONTAINER_multihashmap_put (cache, &entry->key,
364                                                     entry,
365                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
366   cache_size++;
367   return entry;
368 }
369
370
371 static struct GSTCacheGetHandle *
372 search_suitable_cgh (const struct CacheEntry *entry,
373                      const struct GSTCacheGetHandle *head)
374 {
375   const struct GSTCacheGetHandle *cgh;
376
377   for (cgh=head; NULL != cgh; cgh=cgh->next)
378   {
379     if (GNUNET_YES == cgh->notify_called)
380       return NULL;
381     switch (cgh->type)
382     {
383     case CGT_TRANSPORT_HANDLE:
384       if (NULL == entry->transport_handle_)
385         continue;
386       break;
387     case CGT_CORE_HANDLE:
388       if (NULL == entry->core_handle)
389         continue;
390       break;
391     }
392     break;
393   }  
394   return (struct GSTCacheGetHandle *) cgh;
395 }
396
397
398 static void
399 call_cgh_cb (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
400 {
401   struct CacheEntry *entry = cls;
402   struct GSTCacheGetHandle *cgh;
403   const struct GSTCacheGetHandle *cgh2;
404   
405   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != entry->notify_task);
406   entry->notify_task = GNUNET_SCHEDULER_NO_TASK;
407   cgh = search_suitable_cgh (entry, entry->cgh_qhead);
408   GNUNET_assert (NULL != cgh);
409   cgh2 = NULL;
410   if (NULL != cgh->next)
411     cgh2 = search_suitable_cgh (entry, cgh->next);
412   GNUNET_CONTAINER_DLL_remove (entry->cgh_qhead, entry->cgh_qtail, cgh);
413   cgh->notify_called = GNUNET_YES;
414   GNUNET_CONTAINER_DLL_insert_tail (entry->cgh_qhead, entry->cgh_qtail, cgh);
415   if (NULL != cgh2)
416     entry->notify_task = GNUNET_SCHEDULER_add_now (&call_cgh_cb, entry);
417   cgh->cb (cgh->cb_cls, entry->core_handle, 
418            entry->transport_handle_, entry->peer_identity);
419 }
420
421 /**
422  * Function called to notify transport users that another
423  * peer connected to us.
424  *
425  * @param cls closure
426  * @param peer the peer that connected
427  * @param type the type of the handle this notification is for
428  */
429 static void 
430 peer_connect_notify_cb (void *cls,
431                         const struct GNUNET_PeerIdentity *peer,
432                         const enum CacheGetType type)
433 {
434   struct CacheEntry *entry = cls;
435   struct ConnectNotifyContext *ctxt;
436   GST_cache_peer_connect_notify cb;
437   void *cb_cls;
438
439   
440   for (ctxt=entry->nctxt_qhead; NULL != ctxt; ctxt=ctxt->next)
441   {
442     GNUNET_assert (NULL != ctxt->cgh);
443     if (type != ctxt->cgh->type)
444       continue;
445     if (0 == memcmp (ctxt->target, peer, sizeof (struct GNUNET_PeerIdentity)))
446       break;
447   }
448   if (NULL == ctxt)
449     return;
450   cb = ctxt->cb;
451   cb_cls = ctxt->cb_cls;
452   ctxt->cgh->nctxt = NULL;
453   GNUNET_CONTAINER_DLL_remove (entry->nctxt_qhead, entry->nctxt_qtail, ctxt);
454   GNUNET_free (ctxt);
455   cb (cb_cls, peer);
456 }
457
458
459 /**
460  * Function called to notify transport users that another
461  * peer connected to us.
462  *
463  * @param cls closure
464  * @param peer the peer that connected
465  * @param ats performance data
466  * @param ats_count number of entries in ats (excluding 0-termination)
467  */
468 static void 
469 transport_peer_connect_notify_cb (void *cls,
470                                   const struct GNUNET_PeerIdentity *peer,
471                                   const struct GNUNET_ATS_Information *ats,
472                                   uint32_t ats_count)
473 {
474   peer_connect_notify_cb (cls, peer, CGT_TRANSPORT_HANDLE);
475 }
476
477
478 static void
479 opstart_get_handle_transport (void *cls)
480 {
481   struct CacheEntry *entry = cls;
482
483   GNUNET_assert (NULL != entry);
484   LOG_DEBUG ("Opening a transport connection to peer %u\n", entry->peer_id);
485   entry->transport_handle_ = 
486       GNUNET_TRANSPORT_connect (entry->cfg,
487                                 NULL, entry,
488                                 NULL,
489                                 &transport_peer_connect_notify_cb, NULL);
490   if (NULL == entry->transport_handle_)
491   {
492     GNUNET_break (0);
493     return;
494   }
495   //GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == entry->notify_task);
496   if (0 == entry->demand)
497     return;
498   if (GNUNET_NO == entry->cgh_qhead->notify_called)
499     entry->notify_task = GNUNET_SCHEDULER_add_now (&call_cgh_cb, entry);
500 }
501
502
503 static void
504 oprelease_get_handle_transport (void *cls)
505 {
506   struct CacheEntry *entry = cls;
507   
508   if (NULL == entry->transport_handle_)
509     return;
510   GNUNET_TRANSPORT_disconnect (entry->transport_handle_);
511   entry->transport_handle_ = NULL;
512 }
513
514
515 /**
516  * Function called after GNUNET_CORE_connect has succeeded (or failed
517  * for good).  Note that the private key of the peer is intentionally
518  * not exposed here; if you need it, your process should try to read
519  * the private key file directly (which should work if you are
520  * authorized...).  Implementations of this function must not call
521  * GNUNET_CORE_disconnect (other than by scheduling a new task to
522  * do this later).
523  *
524  * @param cls closure
525  * @param server handle to the server, NULL if we failed
526  * @param my_identity ID of this peer, NULL if we failed
527  */
528 static void 
529 core_startup_cb (void *cls,
530                  struct GNUNET_CORE_Handle * server,
531                  const struct GNUNET_PeerIdentity *my_identity)
532 {
533   struct CacheEntry *entry = cls;
534
535   if (NULL == my_identity)
536   {
537     GNUNET_break (0);
538     return;
539   }
540   GNUNET_assert (NULL == entry->peer_identity);
541   entry->core_handle = server;
542   entry->peer_identity = GNUNET_malloc (sizeof (struct GNUNET_PeerIdentity));
543   memcpy (entry->peer_identity, my_identity,
544           sizeof (struct GNUNET_PeerIdentity));
545   if (0 == entry->demand)
546     return;
547   if (GNUNET_NO == entry->cgh_qhead->notify_called)
548     entry->notify_task = GNUNET_SCHEDULER_add_now (&call_cgh_cb, entry);
549 }
550
551
552 /**
553  * Method called whenever a given peer connects.
554  *
555  * @param cls closure
556  * @param peer peer identity this notification is about
557  * @param atsi performance data for the connection
558  * @param atsi_count number of records in 'atsi'
559  */
560 static void 
561 core_peer_connect_cb (void *cls,
562                       const struct GNUNET_PeerIdentity * peer,
563                       const struct GNUNET_ATS_Information * atsi,
564                       unsigned int atsi_count)
565 {  
566   peer_connect_notify_cb (cls, peer, CGT_CORE_HANDLE);
567 }
568
569
570 static void
571 opstart_get_handle_core (void *cls)
572 {
573   struct CacheEntry *entry = cls;
574   const struct GNUNET_CORE_MessageHandler no_handlers[] = {
575     {NULL, 0, 0}
576   };
577
578   GNUNET_assert (NULL != entry);
579   LOG_DEBUG ("Opening a CORE connection to peer %u\n", entry->peer_id);
580   /* void?: We also get the handle when the connection to CORE is successful */
581   (void) GNUNET_CORE_connect (entry->cfg,
582                               entry,
583                               &core_startup_cb,
584                               &core_peer_connect_cb,
585                               NULL, /* disconnect cb */
586                               NULL, /* inbound notify */
587                               GNUNET_NO,
588                               NULL, /* outbound notify */
589                               GNUNET_NO,
590                               no_handlers);
591   if (NULL == entry->core_handle)
592   {
593     GNUNET_break (0);
594     return;
595   }
596   //GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == entry->notify_task);
597 }
598
599
600 static void
601 oprelease_get_handle_core (void *cls)
602 {
603   struct CacheEntry *entry = cls;
604   
605   if (NULL == entry->core_handle)
606     return;
607   GNUNET_CORE_disconnect (entry->core_handle);
608   entry->core_handle = NULL;
609   GNUNET_free_non_null (entry->peer_identity);
610   entry->peer_identity = NULL;
611 }
612
613
614 static struct GSTCacheGetHandle *
615 cache_get_handle (unsigned int peer_id,
616                   struct GSTCacheGetHandle *cgh,
617                   const struct GNUNET_CONFIGURATION_Handle *cfg,
618                   const struct GNUNET_PeerIdentity *target,
619                   GST_cache_peer_connect_notify connect_notify_cb,
620                   void *connect_notify_cb_cls)
621 {
622   struct GNUNET_HashCode key;
623   void *handle;
624   struct CacheEntry *entry;
625   struct ConnectNotifyContext *ctxt;
626   struct GNUNET_TESTBED_Operation *op;
627
628   GNUNET_assert (0 != cgh->type);
629   GNUNET_CRYPTO_hash (&peer_id, sizeof (peer_id), &key);
630   handle = NULL;
631   entry = NULL;
632   switch (cgh->type)
633   {
634   case CGT_TRANSPORT_HANDLE:
635     entry = cache_lookup_handles (&key, (struct GNUNET_TRANSPORT_Handle **)
636                                   &handle, NULL);
637     if (NULL != handle)
638       LOG_DEBUG ("Found TRANSPORT handle in cache for peer %u\n", entry->peer_id);
639     break;
640   case CGT_CORE_HANDLE:
641     entry = cache_lookup_handles (&key, NULL, 
642                                   (struct GNUNET_CORE_Handle **) &handle);
643     if (NULL != handle)
644       LOG_DEBUG ("Found CORE handle in cache for peer %u\n", entry->peer_id);
645     break;
646   }
647   if (NULL != handle)
648   {
649     GNUNET_assert (NULL != entry);
650     if (0 == entry->demand)
651       GNUNET_CONTAINER_DLL_remove (lru_cache_head, lru_cache_tail, entry);
652   }
653   if (NULL == entry)
654     entry = add_entry (&key, peer_id);
655   if (NULL == entry->cfg)
656     entry->cfg = GNUNET_CONFIGURATION_dup (cfg);
657   entry->demand++;
658   cgh->entry = entry;
659   GNUNET_CONTAINER_DLL_insert (entry->cgh_qhead, entry->cgh_qtail, cgh);
660   if ((NULL != target) && (NULL != connect_notify_cb))
661   {
662     ctxt = GNUNET_malloc (sizeof (struct ConnectNotifyContext));
663     ctxt->target = target;
664     ctxt->cb = connect_notify_cb;
665     ctxt->cb_cls = connect_notify_cb_cls;
666     GNUNET_assert (NULL == cgh->nctxt);
667     cgh->nctxt = ctxt;
668     ctxt->cgh = cgh;
669     GNUNET_CONTAINER_DLL_insert_tail (entry->nctxt_qhead, entry->nctxt_qtail, ctxt);
670   }
671   if (NULL != handle)
672   {
673     if (GNUNET_SCHEDULER_NO_TASK == entry->notify_task)
674       entry->notify_task = GNUNET_SCHEDULER_add_now (&call_cgh_cb, entry);
675     return cgh;
676   }
677   switch (cgh->type)
678   {
679   case CGT_TRANSPORT_HANDLE:
680     if (NULL != entry->transport_op_)
681       return cgh;
682     op = GNUNET_TESTBED_operation_create_ (entry, &opstart_get_handle_transport,
683                                            &oprelease_get_handle_transport);
684     entry->transport_op_ = op;
685     break;
686   case CGT_CORE_HANDLE:
687     if (NULL != entry->core_op)
688       return cgh;
689     op = GNUNET_TESTBED_operation_create_ (entry, &opstart_get_handle_core,
690                                            &oprelease_get_handle_core);
691     entry->core_op = op;
692     break;
693   }
694   GNUNET_TESTBED_operation_queue_insert_ (GST_opq_openfds, op);
695   GNUNET_TESTBED_operation_begin_wait_ (op);
696   return cgh;
697 }
698
699 /**
700  * Iterator over hash map entries.
701  *
702  * @param cls closure
703  * @param key current key code
704  * @param value value in the hash map
705  * @return GNUNET_YES if we should continue to
706  *         iterate,
707  *         GNUNET_NO if not.
708  */
709 static int
710 cache_clear_iterator (void *cls,
711                       const struct GNUNET_HashCode * key,
712                       void *value)
713 {
714   struct CacheEntry *entry = value;
715   static unsigned int ncleared;
716
717   GNUNET_assert (NULL != entry);
718   GNUNET_break (0 == entry->demand);
719   LOG_DEBUG ("Clearing entry %u of %u\n", ++ncleared, cache_size);
720   GNUNET_CONTAINER_multihashmap_remove (cache, key, value);
721   if (0 == entry->demand)
722     cache_remove (entry);
723   GNUNET_free_non_null (entry->hello);
724   GNUNET_break (NULL == entry->transport_handle_);
725   GNUNET_break (NULL == entry->transport_op_);
726   GNUNET_break (NULL == entry->core_handle);
727   GNUNET_break (NULL == entry->core_op);
728   GNUNET_break (NULL == entry->cfg);
729   GNUNET_assert (NULL == entry->cgh_qhead);
730   GNUNET_assert (NULL == entry->cgh_qtail);
731   GNUNET_assert (NULL == entry->nctxt_qhead);
732   GNUNET_assert (NULL == entry->nctxt_qtail);
733   GNUNET_free (entry);
734   return GNUNET_YES;
735 }
736
737
738 /**
739  * Clear cache
740  */
741 void
742 GST_cache_clear ()
743 {
744   GNUNET_CONTAINER_multihashmap_iterate (cache, &cache_clear_iterator, NULL);
745   GNUNET_assert (0 == GNUNET_CONTAINER_multihashmap_size (cache));
746   GNUNET_CONTAINER_multihashmap_destroy (cache);
747 }
748
749
750 /**
751  * Initializes the cache
752  *
753  * @param size the size of the cache
754  */
755 void
756 GST_cache_init (unsigned int size)
757 {
758   if (0 == size)
759     return;
760   lru_cache_threshold_size = size;
761   if (size > 1)
762     size = size / 2;
763   cache = GNUNET_CONTAINER_multihashmap_create (size, GNUNET_YES);
764 }
765
766
767 /**
768  * Mark the GetCacheHandle as being done if a handle has been provided already
769  * or as being cancelled if the callback for the handle hasn't been called.
770  *
771  * @param cgh the CacheGetHandle handle
772  */
773 void
774 GST_cache_get_handle_done (struct GSTCacheGetHandle *cgh)
775 {
776   struct CacheEntry *entry;
777
778   entry = cgh->entry;
779   GNUNET_assert (NULL != entry);
780   GNUNET_assert (0 < entry->demand);
781   entry->demand--;
782   if (GNUNET_SCHEDULER_NO_TASK != entry->notify_task)
783   {
784     GNUNET_SCHEDULER_cancel (entry->notify_task);
785     entry->notify_task = GNUNET_SCHEDULER_NO_TASK;
786   }
787   GNUNET_CONTAINER_DLL_remove (entry->cgh_qhead, entry->cgh_qtail, cgh);
788   if (NULL != cgh->nctxt)
789   {
790     GNUNET_assert (cgh == cgh->nctxt->cgh);
791     GNUNET_CONTAINER_DLL_remove (entry->nctxt_qhead, entry->nctxt_qtail, cgh->nctxt);
792     GNUNET_free (cgh->nctxt);
793   }
794   
795   if (0 == entry->demand)
796   {
797     GNUNET_CONTAINER_DLL_insert_tail (lru_cache_head, lru_cache_tail, entry);
798     if (lru_cache_size > lru_cache_threshold_size)
799       cache_remove (lru_cache_head);
800   }
801   else
802   {
803     if (GNUNET_NO == entry->cgh_qhead->notify_called)
804       entry->notify_task = GNUNET_SCHEDULER_add_now (&call_cgh_cb, entry);
805   }
806   GNUNET_free (cgh);
807 }
808
809
810 /**
811  * Get a transport handle with the given configuration. If the handle is already
812  * cached before, it will be retured in the given callback; the peer_id is used to lookup in the
813  * cache. If not a new operation is started to open the transport handle and
814  * will be given in the callback when it is available.
815  *
816  * @param peer_id the index of the peer
817  * @param cfg the configuration with which the transport handle has to be
818  *          created if it was not present in the cache
819  * @param cb the callback to notify when the transport handle is available
820  * @param cb_cls the closure for the above callback
821  * @param target the peer identify of the peer whose connection to our TRANSPORT
822  *          subsystem will be notified through the connect_notify_cb. Can be NULL
823  * @param connect_notify_cb the callback to call when the given target peer is
824  *          connected. This callback will only be called once or never again (in
825  *          case the target peer cannot be connected). Can be NULL
826  * @param connect_notify_cb_cls the closure for the above callback
827  * @return the handle which can be used cancel or mark that the handle is no
828  *           longer being used
829  */
830 struct GSTCacheGetHandle *
831 GST_cache_get_handle_transport (unsigned int peer_id,
832                                 const struct GNUNET_CONFIGURATION_Handle *cfg,
833                                 GST_cache_callback cb,
834                                 void *cb_cls,
835                                 const struct GNUNET_PeerIdentity *target,
836                                 GST_cache_peer_connect_notify connect_notify_cb,
837                                 void *connect_notify_cb_cls)
838 {
839   struct GSTCacheGetHandle *cgh;
840
841   cgh = GNUNET_malloc (sizeof (struct GSTCacheGetHandle));
842   cgh->cb = cb;
843   cgh->cb_cls = cb_cls;
844   cgh->type = CGT_TRANSPORT_HANDLE;
845   return cache_get_handle (peer_id, cgh, cfg,
846                            target, connect_notify_cb, connect_notify_cb_cls);
847 }
848
849
850 /**
851  * Get a CORE handle with the given configuration. If the handle is already
852  * cached before, it will be retured in the given callback; the peer_id is used
853  * to lookup in the cache. If the handle is not cached before, a new operation
854  * is started to open the CORE handle and will be given in the callback when it
855  * is available along with the peer identity
856  *
857  * @param peer_id the index of the peer
858  * @param cfg the configuration with which the transport handle has to be
859  *          created if it was not present in the cache
860  * @param cb the callback to notify when the transport handle is available
861  * @param cb_cls the closure for the above callback
862  * @param target the peer identify of the peer whose connection to our CORE
863  *          subsystem will be notified through the connect_notify_cb. Can be NULL
864  * @param connect_notify_cb the callback to call when the given target peer is
865  *          connected. This callback will only be called once or never again (in
866  *          case the target peer cannot be connected). Can be NULL
867  * @param connect_notify_cb_cls the closure for the above callback
868  * @return the handle which can be used cancel or mark that the handle is no
869  *           longer being used
870  */
871 struct GSTCacheGetHandle *
872 GST_cache_get_handle_core (unsigned int peer_id,
873                            const struct GNUNET_CONFIGURATION_Handle *cfg,
874                            GST_cache_callback cb,
875                            void *cb_cls,
876                            const struct GNUNET_PeerIdentity *target,
877                            GST_cache_peer_connect_notify connect_notify_cb,
878                            void *connect_notify_cb_cls)
879 {
880   struct GSTCacheGetHandle *cgh;
881
882   cgh = GNUNET_malloc (sizeof (struct GSTCacheGetHandle));
883   cgh->cb = cb;
884   cgh->cb_cls = cb_cls;
885   cgh->type = CGT_CORE_HANDLE;
886   return cache_get_handle (peer_id, cgh, cfg,
887                            target, connect_notify_cb, connect_notify_cb_cls);
888 }
889
890
891 /**
892  * Looks up in the hello cache and returns the HELLO of the given peer
893  *
894  * @param peer_id the index of the peer whose HELLO has to be looked up
895  * @return the HELLO message; NULL if not found
896  */
897 const struct GNUNET_MessageHeader *
898 GST_cache_lookup_hello (const unsigned int peer_id)
899 {
900   struct CacheEntry *entry;
901   struct GNUNET_HashCode key;
902   
903   LOG_DEBUG ("Looking up HELLO for peer %u\n", peer_id);
904   GNUNET_CRYPTO_hash (&peer_id, sizeof (peer_id), &key);
905   entry = cache_lookup (&key);
906   if (NULL == entry)
907     return NULL;
908   if (NULL != entry->hello)
909     LOG_DEBUG ("HELLO found for peer %u\n", peer_id);
910   return entry->hello;
911 }
912
913
914 /**
915  * Caches the HELLO of the given peer. Updates the HELLO if it was already
916  * cached before
917  *
918  * @param id the peer identity of the peer whose HELLO has to be cached
919  * @param hello the HELLO message
920  */
921 void
922 GST_cache_add_hello (const unsigned int peer_id,
923                      const struct GNUNET_MessageHeader *hello)
924 {
925   struct CacheEntry *entry;
926   struct GNUNET_HashCode key;
927
928   GNUNET_CRYPTO_hash (&peer_id, sizeof (peer_id), &key);
929   entry = GNUNET_CONTAINER_multihashmap_get (cache, &key);
930   if (NULL == entry)
931     entry = add_entry (&key, peer_id);
932   GNUNET_free_non_null (entry->hello);
933   entry->hello = GNUNET_copy_message (hello);
934 }
935
936 /* end of gnunet-service-testbed_hc.c */