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