-typo-doxygen
[oweals/gnunet.git] / src / testbed / gnunet-service-testbed_connectionpool.c
1 /*
2   This file is part of GNUnet.
3   Copyright (C) 2008--2015 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 3, 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., 51 Franklin Street, Fifth Floor,
18   Boston, MA 02110-1301, USA.
19 */
20
21 /**
22  * @file testbed/gnunet-service-testbed_connectionpool.c
23  * @brief connection pooling for connections to peers' services
24  * @author Sree Harsha Totakura <sreeharsha@totakura.in>
25  */
26
27 #include "gnunet-service-testbed.h"
28 #include "gnunet-service-testbed_connectionpool.h"
29 #include "testbed_api_operations.h"
30
31 /**
32  * Redefine LOG with a changed log component string
33  */
34 #ifdef LOG
35 #undef LOG
36 #endif
37 #define LOG(kind,...)                                   \
38   GNUNET_log_from (kind, "testbed-connectionpool", __VA_ARGS__)
39
40
41 /**
42  * Time to expire a cache entry
43  */
44 #define CACHE_EXPIRY                            \
45   GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
46
47
48 /**
49  * The request handle for obtaining a pooled connection
50  */
51 struct GST_ConnectionPool_GetHandle;
52
53
54 /**
55  * A pooled connection
56  */
57 struct PooledConnection
58 {
59   /**
60    * Next ptr for placing this object in the DLL of least recently used pooled
61    * connections
62    */
63   struct PooledConnection *next;
64
65   /**
66    * Prev ptr for placing this object in the DLL of the least recently used
67    * pooled connections
68    */
69   struct PooledConnection *prev;
70
71   /**
72    * The transport handle to the peer corresponding to this entry; can be NULL
73    */
74   struct GNUNET_TRANSPORT_Handle *handle_transport;
75
76   /**
77    * The core handle to the peer corresponding to this entry; can be NULL
78    */
79   struct GNUNET_CORE_Handle *handle_core;
80
81   /**
82    * The ATS handle to the peer correspondign to this entry; can be NULL.
83    */
84   struct GNUNET_ATS_ConnectivityHandle *handle_ats_connectivity;
85
86   /**
87    * The operation handle for transport handle
88    */
89   struct GNUNET_TESTBED_Operation *op_transport;
90
91   /**
92    * The operation handle for core handle
93    */
94   struct GNUNET_TESTBED_Operation *op_core;
95
96   /**
97    * The operation handle for ATS handle
98    */
99   struct GNUNET_TESTBED_Operation *op_ats_connectivity;
100
101   /**
102    * The peer identity of this peer. Will be set upon opening a connection to
103    * the peers CORE service. Will be NULL until then and after the CORE
104    * connection is closed
105    */
106   struct GNUNET_PeerIdentity *peer_identity;
107
108   /**
109    * The configuration of the peer. Should be not NULL as long as the core_handle
110    * or transport_handle are valid
111    */
112   struct GNUNET_CONFIGURATION_Handle *cfg;
113
114   /**
115    * DLL head for the queue to serve notifications when a peer is connected
116    */
117   struct GST_ConnectionPool_GetHandle *head_notify;
118
119   /**
120    * DLL tail for the queue to serve notifications when a peer is connected
121    */
122   struct GST_ConnectionPool_GetHandle *tail_notify;
123
124   /**
125    * DLL head for the queue of #GST_ConnectionPool_GetHandle requests that are
126    * waiting for this connection to be opened
127    */
128   struct GST_ConnectionPool_GetHandle *head_waiting;
129
130   /**
131    * DLL tail for the queue of #GST_ConnectionPool_GetHandle requests that are
132    * waiting for this connection to be opened
133    */
134   struct GST_ConnectionPool_GetHandle *tail_waiting;
135
136   /**
137    * The task to expire this connection from the connection pool
138    */
139   struct GNUNET_SCHEDULER_Task * expire_task;
140
141   /**
142    * The task to notify a waiting #GST_ConnectionPool_GetHandle object
143    */
144   struct GNUNET_SCHEDULER_Task * notify_task;
145
146   /**
147    * Number of active requests using this pooled connection
148    */
149   unsigned int demand;
150
151   /**
152    * Is this entry in LRU
153    */
154   int in_lru;
155
156   /**
157    * Is this entry present in the connection pool
158    */
159   int in_pool;
160
161   /**
162    * The index of this peer
163    */
164   uint32_t index;
165 };
166
167
168 /**
169  * The request handle for obtaining a pooled connection
170  */
171 struct GST_ConnectionPool_GetHandle
172 {
173   /**
174    * The next ptr for inclusion in the notification DLLs.  At first the object
175    * is placed in the waiting DLL of the corresponding #PooledConnection
176    * object.  After the handle is opened it is moved to the notification DLL if
177    * @p connect_notify_cb and @p target are not NULL
178    */
179   struct GST_ConnectionPool_GetHandle *next;
180
181   /**
182    * The prev ptr for inclusion in the notification DLLs
183    */
184   struct GST_ConnectionPool_GetHandle *prev;
185
186   /**
187    * The pooled connection object this handle corresponds to
188    */
189   struct PooledConnection *entry;
190
191   /**
192    * The cache callback to call when a handle is available
193    */
194   GST_connection_pool_connection_ready_cb cb;
195
196   /**
197    * The closure for the above callback
198    */
199   void *cb_cls;
200
201   /**
202    * The peer identity of the target peer. When this target peer is connected,
203    * call the notify callback
204    */
205   const struct GNUNET_PeerIdentity *target;
206
207   /**
208    * The callback to be called for serving notification that the target peer is
209    * connected
210    */
211   GST_connection_pool_peer_connect_notify connect_notify_cb;
212
213   /**
214    * The closure for the notify callback
215    */
216   void *connect_notify_cb_cls;
217
218   /**
219    * The service we want to connect to
220    */
221   enum GST_ConnectionPool_Service service;
222
223   /**
224    * Did we call the pool_connection_ready_cb already?
225    */
226   int connection_ready_called;
227
228   /**
229    * Are we waiting for any peer connect notifications?
230    */
231   int notify_waiting;
232 };
233
234
235 /**
236  * A hashmap for quickly finding connections in the connection pool
237  */
238 static struct GNUNET_CONTAINER_MultiHashMap32 *map;
239
240 /**
241  * DLL head for maitaining the least recently used #PooledConnection objects.
242  * The head is the least recently used object.
243  */
244 static struct PooledConnection *head_lru;
245
246 /**
247  * DLL tail for maitaining the least recently used #PooledConnection objects
248  */
249 static struct PooledConnection *tail_lru;
250
251 /**
252  * DLL head for maintaining #PooledConnection objects that are not added into
253  * the connection pool as it was full at the time the object's creation
254  * FIXME
255  */
256 static struct PooledConnection *head_not_pooled;
257
258 /**
259  * DLL tail for maintaining #PooledConnection objects that are not added into
260  * the connection pool as it was full at the time the object's creation
261  */
262 static struct PooledConnection *tail_not_pooled;
263
264 /**
265  * The maximum number of entries that can be present in the connection pool
266  */
267 static unsigned int max_size;
268
269
270 /**
271  * Cancel the expiration task of the give #PooledConnection object
272  *
273  * @param entry the #PooledConnection object
274  */
275 static void
276 expire_task_cancel (struct PooledConnection *entry);
277
278
279 /**
280  * Destroy a #PooledConnection object
281  *
282  * @param entry the #PooledConnection object
283  */
284 static void
285 destroy_pooled_connection (struct PooledConnection *entry)
286 {
287   GNUNET_assert ((NULL == entry->head_notify) && (NULL == entry->tail_notify));
288   GNUNET_assert ((NULL == entry->head_waiting) && (NULL ==
289                                                    entry->tail_waiting));
290   GNUNET_assert (0 == entry->demand);
291   expire_task_cancel (entry);
292   if (entry->in_lru)
293     GNUNET_CONTAINER_DLL_remove (head_lru, tail_lru, entry);
294   if (entry->in_pool)
295     GNUNET_assert (GNUNET_OK ==
296                    GNUNET_CONTAINER_multihashmap32_remove (map,
297                                                            entry->index,
298                                                            entry));
299   if (NULL != entry->notify_task)
300   {
301     GNUNET_SCHEDULER_cancel (entry->notify_task);
302     entry->notify_task = NULL;
303   }
304   LOG_DEBUG ("Cleaning up handles of a pooled connection\n");
305   if (NULL != entry->handle_transport)
306     GNUNET_assert (NULL != entry->op_transport);
307   if (NULL != entry->op_transport)
308   {
309     GNUNET_TESTBED_operation_done (entry->op_transport);
310     entry->op_transport = NULL;
311   }
312   if (NULL != entry->handle_ats_connectivity)
313     GNUNET_assert (NULL != entry->op_ats_connectivity);
314   if (NULL != entry->op_ats_connectivity)
315   {
316     GNUNET_TESTBED_operation_done (entry->op_ats_connectivity);
317     entry->op_ats_connectivity = NULL;
318   }
319   if (NULL != entry->op_core)
320   {
321     GNUNET_TESTBED_operation_done (entry->op_core);
322     entry->op_core = NULL;
323   }
324   GNUNET_assert (NULL == entry->handle_core);
325   GNUNET_assert (NULL == entry->handle_ats_connectivity);
326   GNUNET_assert (NULL == entry->handle_transport);
327   GNUNET_CONFIGURATION_destroy (entry->cfg);
328   GNUNET_free (entry);
329 }
330
331
332 /**
333  * Expire a #PooledConnection object
334  *
335  * @param cls the #PooledConnection object
336  * @param tc scheduler task context
337  */
338 static void
339 expire (void *cls,
340         const struct GNUNET_SCHEDULER_TaskContext *tc)
341 {
342   struct PooledConnection *entry = cls;
343
344   entry->expire_task = NULL;
345   destroy_pooled_connection (entry);
346 }
347
348
349 /**
350  * Cancel the expiration task of the give #PooledConnection object
351  *
352  * @param entry the #PooledConnection object
353  */
354 static void
355 expire_task_cancel (struct PooledConnection *entry)
356 {
357   if (NULL != entry->expire_task)
358   {
359     GNUNET_SCHEDULER_cancel (entry->expire_task);
360     entry->expire_task = NULL;
361   }
362 }
363
364
365 /**
366  * Function to add a #PooledConnection object into LRU and begin the expiry task
367  *
368  * @param entry the #PooledConnection object
369  */
370 static void
371 add_to_lru (struct PooledConnection *entry)
372 {
373   GNUNET_assert (0 == entry->demand);
374   GNUNET_assert (!entry->in_lru);
375   GNUNET_CONTAINER_DLL_insert_tail (head_lru, tail_lru, entry);
376   entry->in_lru = GNUNET_YES;
377   GNUNET_assert (NULL == entry->expire_task);
378   entry->expire_task = GNUNET_SCHEDULER_add_delayed (CACHE_EXPIRY,
379                                                      &expire, entry);
380 }
381
382
383 /**
384  * Function to find a #GST_ConnectionPool_GetHandle which is waiting for one of
385  * the handles in given entry which are now available.
386  *
387  * @param entry the pooled connection whose active list has to be searched
388  * @param head the starting list element in the GSTCacheGetHandle where the
389  *          search has to be begin
390  * @return a suitable GSTCacheGetHandle whose handle ready notify callback
391  *           hasn't been called yet. NULL if no such suitable GSTCacheGetHandle
392  *           is found
393  */
394 static struct GST_ConnectionPool_GetHandle *
395 search_waiting (const struct PooledConnection *entry,
396                 struct GST_ConnectionPool_GetHandle *head)
397 {
398   struct GST_ConnectionPool_GetHandle *gh;
399
400   for (gh = head; NULL != gh; gh = gh->next)
401   {
402     switch (gh->service)
403     {
404     case GST_CONNECTIONPOOL_SERVICE_CORE:
405       if (NULL == entry->handle_core)
406         continue;
407       if (NULL == entry->peer_identity)
408         continue;               /* CORE connection isn't ready yet */
409       break;
410     case GST_CONNECTIONPOOL_SERVICE_TRANSPORT:
411       if (NULL == entry->handle_transport)
412         continue;
413       break;
414     case GST_CONNECTIONPOOL_SERVICE_ATS_CONNECTIVITY:
415       if (NULL == entry->handle_ats_connectivity)
416         continue;
417       break;
418     }
419     break;
420   }
421   return gh;
422 }
423
424
425 /**
426  * A handle in the #PooledConnection object pointed by @a cls is ready and there
427  * is a #GST_ConnectionPool_GetHandle object waiting in the waiting list.  This
428  * function retrieves that object and calls the handle ready callback.  It
429  * further schedules itself if there are similar waiting objects which can be notified.
430  *
431  * @param cls the #PooledConnection object
432  * @param tc the task context from scheduler
433  */
434 static void
435 connection_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
436 {
437   struct PooledConnection *entry = cls;
438   struct GST_ConnectionPool_GetHandle *gh;
439   struct GST_ConnectionPool_GetHandle *gh_next;
440
441   GNUNET_assert (NULL != entry->notify_task);
442   entry->notify_task = NULL;
443   gh = search_waiting (entry, entry->head_waiting);
444   GNUNET_assert (NULL != gh);
445   gh_next = NULL;
446   if (NULL != gh->next)
447     gh_next = search_waiting (entry, gh->next);
448   GNUNET_CONTAINER_DLL_remove (entry->head_waiting,
449                                entry->tail_waiting,
450                                gh);
451   gh->connection_ready_called = 1;
452   if (NULL != gh_next)
453     entry->notify_task = GNUNET_SCHEDULER_add_now (&connection_ready,
454                                                    entry);
455   if ( (NULL != gh->target) &&
456        (NULL != gh->connect_notify_cb) )
457   {
458     GNUNET_CONTAINER_DLL_insert_tail (entry->head_notify,
459                                       entry->tail_notify,
460                                       gh);
461     gh->notify_waiting = 1;
462   }
463   LOG_DEBUG ("Connection ready for handle type %u\n",
464              gh->service);
465   gh->cb (gh->cb_cls,
466           entry->handle_core,
467           entry->handle_transport,
468           entry->handle_ats_connectivity,
469           entry->peer_identity);
470 }
471
472
473 /**
474  * Function called from peer connect notify callbacks from CORE and TRANSPORT
475  * connections. This function calls the pending peer connect notify callbacks
476  * which are queued in an entry.
477  *
478  * @param cls the #PooledConnection object
479  * @param peer the peer that connected
480  * @param service the service where this notification has originated
481  */
482 static void
483 peer_connect_notify_cb (void *cls,
484                         const struct GNUNET_PeerIdentity *peer,
485                         const enum GST_ConnectionPool_Service service)
486 {
487   struct PooledConnection *entry = cls;
488   struct GST_ConnectionPool_GetHandle *gh;
489   struct GST_ConnectionPool_GetHandle *gh_next;
490   GST_connection_pool_peer_connect_notify cb;
491   void *cb_cls;
492
493   for (gh = entry->head_notify; NULL != gh;)
494   {
495     GNUNET_assert (NULL != gh->target);
496     GNUNET_assert (NULL != gh->connect_notify_cb);
497     GNUNET_assert (gh->connection_ready_called);
498     if (service != gh->service)
499     {
500       gh = gh->next;
501       continue;
502     }
503     if (0 != memcmp (gh->target,
504                      peer,
505                      sizeof (struct GNUNET_PeerIdentity)))
506     {
507       gh = gh->next;
508       continue;
509     }
510     cb = gh->connect_notify_cb;
511     cb_cls = gh->connect_notify_cb_cls;
512     gh_next = gh->next;
513     GNUNET_CONTAINER_DLL_remove (entry->head_notify, entry->tail_notify, gh);
514     gh->notify_waiting = 0;
515     LOG_DEBUG ("Peer connected to peer %u at service %u\n",
516                entry->index,
517                gh->service);
518     gh = gh_next;
519     cb (cb_cls, peer);
520   }
521 }
522
523
524 /**
525  * Function called to notify transport users that another
526  * peer connected to us.
527  *
528  * @param cls the #PooledConnection object
529  * @param peer the peer that connected
530  */
531 static void
532 transport_peer_connect_notify_cb (void *cls,
533                                   const struct GNUNET_PeerIdentity *peer)
534 {
535   struct PooledConnection *entry = cls;
536
537   peer_connect_notify_cb (entry,
538                           peer,
539                           GST_CONNECTIONPOOL_SERVICE_TRANSPORT);
540 }
541
542
543 /**
544  * Function called when resources for opening a connection to TRANSPORT are
545  * available.
546  *
547  * @param cls the #PooledConnection object
548  */
549 static void
550 opstart_get_handle_transport (void *cls)
551 {
552   struct PooledConnection *entry = cls;
553
554   GNUNET_assert (NULL != entry);
555   LOG_DEBUG ("Opening a transport connection to peer %u\n", entry->index);
556   entry->handle_transport =
557       GNUNET_TRANSPORT_connect (entry->cfg, NULL, entry, NULL,
558                                 &transport_peer_connect_notify_cb, NULL);
559   if (NULL == entry->handle_transport)
560   {
561     GNUNET_break (0);
562     return;
563   }
564   if (0 == entry->demand)
565     return;
566   if (NULL != entry->notify_task)
567     return;
568   if (NULL != search_waiting (entry, entry->head_waiting))
569   {
570     entry->notify_task = GNUNET_SCHEDULER_add_now (&connection_ready, entry);
571     return;
572   }
573 }
574
575
576 /**
577  * Function called when the operation responsible for opening a TRANSPORT
578  * connection is marked as done.
579  *
580  * @param cls the cache entry
581  */
582 static void
583 oprelease_get_handle_transport (void *cls)
584 {
585   struct PooledConnection *entry = cls;
586
587   if (NULL == entry->handle_transport)
588     return;
589   GNUNET_TRANSPORT_disconnect (entry->handle_transport);
590   entry->handle_transport = NULL;
591 }
592
593
594 /**
595  * Method called whenever a given peer connects at CORE level
596  *
597  * @param cls the #PooledConnection object
598  * @param peer peer identity this notification is about
599  */
600 static void
601 core_peer_connect_cb (void *cls, const struct GNUNET_PeerIdentity *peer)
602 {
603   struct PooledConnection *entry = cls;
604
605   peer_connect_notify_cb (entry, peer, GST_CONNECTIONPOOL_SERVICE_CORE);
606 }
607
608
609 /**
610  * Function called after #GNUNET_CORE_connect() has succeeded (or failed
611  * for good).  Note that the private key of the peer is intentionally
612  * not exposed here; if you need it, your process should try to read
613  * the private key file directly (which should work if you are
614  * authorized...).  Implementations of this function must not call
615  * #GNUNET_CORE_disconnect() (other than by scheduling a new task to
616  * do this later).
617  *
618  * @param cls the #PooledConnection object
619  * @param my_identity ID of this peer, NULL if we failed
620  */
621 static void
622 core_startup_cb (void *cls,
623                  const struct GNUNET_PeerIdentity *my_identity)
624 {
625   struct PooledConnection *entry = cls;
626
627   if (NULL == my_identity)
628   {
629     GNUNET_break (0);
630     return;
631   }
632   GNUNET_assert (NULL == entry->peer_identity);
633   entry->peer_identity = GNUNET_new (struct GNUNET_PeerIdentity);
634   memcpy (entry->peer_identity,
635           my_identity,
636           sizeof (struct GNUNET_PeerIdentity));
637   if (0 == entry->demand)
638     return;
639   if (NULL != entry->notify_task)
640     return;
641   if (NULL != search_waiting (entry, entry->head_waiting))
642   {
643     entry->notify_task = GNUNET_SCHEDULER_add_now (&connection_ready, entry);
644     return;
645   }
646 }
647
648
649 /**
650  * Function called when resources for opening a connection to CORE are
651  * available.
652  *
653  * @param cls the #PooledConnection object
654  */
655 static void
656 opstart_get_handle_core (void *cls)
657 {
658   struct PooledConnection *entry = cls;
659   const struct GNUNET_CORE_MessageHandler no_handlers[] = {
660     {NULL, 0, 0}
661   };
662
663   GNUNET_assert (NULL != entry);
664   LOG_DEBUG ("Opening a CORE connection to peer %u\n", entry->index);
665   entry->handle_core =
666       GNUNET_CORE_connect (entry->cfg, entry,        /* closure */
667                            &core_startup_cb, /* core startup notify */
668                            &core_peer_connect_cb,    /* peer connect notify */
669                            NULL,     /* peer disconnect notify */
670                            NULL,     /* inbound notify */
671                            GNUNET_NO,        /* inbound header only? */
672                            NULL,     /* outbound notify */
673                            GNUNET_NO,        /* outbound header only? */
674                            no_handlers);
675 }
676
677
678 /**
679  * Function called when the operation responsible for opening a CORE
680  * connection is marked as done.
681  *
682  * @param cls the #PooledConnection object
683  */
684 static void
685 oprelease_get_handle_core (void *cls)
686 {
687   struct PooledConnection *entry = cls;
688
689   if (NULL == entry->handle_core)
690     return;
691   GNUNET_CORE_disconnect (entry->handle_core);
692   entry->handle_core = NULL;
693   GNUNET_free_non_null (entry->peer_identity);
694   entry->peer_identity = NULL;
695 }
696
697
698 /**
699  * Function called when resources for opening a connection to ATS are
700  * available.
701  *
702  * @param cls the #PooledConnection object
703  */
704 static void
705 opstart_get_handle_ats_connectivity (void *cls)
706 {
707   struct PooledConnection *entry = cls;
708
709   entry->handle_ats_connectivity =
710     GNUNET_ATS_connectivity_init (entry->cfg);
711 }
712
713
714 /**
715  * Function called when the operation responsible for opening a ATS
716  * connection is marked as done.
717  *
718  * @param cls the #PooledConnection object
719  */
720 static void
721 oprelease_get_handle_ats_connectivity (void *cls)
722 {
723   struct PooledConnection *entry = cls;
724
725   if (NULL == entry->handle_ats_connectivity)
726     return;
727   GNUNET_ATS_connectivity_done (entry->handle_ats_connectivity);
728   entry->handle_ats_connectivity = NULL;
729 }
730
731
732 /**
733  * This function will be called for every #PooledConnection object in @p map
734  *
735  * @param cls NULL
736  * @param key current key code
737  * @param value the #PooledConnection object
738  * @return #GNUNET_YES if we should continue to
739  *         iterate,
740  *         #GNUNET_NO if not.
741  */
742 static int
743 cleanup_iterator (void *cls,
744                   uint32_t key,
745                   void *value)
746 {
747   struct PooledConnection *entry = value;
748
749   GNUNET_assert (NULL != entry);
750   destroy_pooled_connection (entry);
751   return GNUNET_YES;
752 }
753
754
755 /**
756  * Initialise the connection pool.
757  *
758  * @param size the size of the connection pool.  Each entry in the connection
759  *   pool can handle a connection to each of the services enumerated in
760  *   #GST_ConnectionPool_Service
761  */
762 void
763 GST_connection_pool_init (unsigned int size)
764 {
765   max_size = size;
766   if (0 == max_size)
767     return;
768   GNUNET_assert (NULL == map);
769   map = GNUNET_CONTAINER_multihashmap32_create (((size * 3) / 4) + 1);
770 }
771
772
773 /**
774  * Cleanup the connection pool
775  */
776 void
777 GST_connection_pool_destroy ()
778 {
779   struct PooledConnection *entry;
780
781   if (NULL != map)
782   {
783     GNUNET_assert (GNUNET_SYSERR !=
784                    GNUNET_CONTAINER_multihashmap32_iterate (map,
785                                                             &cleanup_iterator,
786                                                             NULL));
787     GNUNET_CONTAINER_multihashmap32_destroy (map);
788     map = NULL;
789   }
790   while (NULL != (entry = head_lru))
791   {
792     GNUNET_CONTAINER_DLL_remove (head_lru, tail_lru, entry);
793     destroy_pooled_connection (entry);
794   }
795   GNUNET_assert (NULL == head_not_pooled);
796 }
797
798
799 /**
800  * Get a connection handle to @a service.  If the connection is opened before
801  * and the connection handle is present in the connection pool, it is returned
802  * through @a cb.  @a peer_id is used for the lookup in the connection pool.  If
803  * the connection handle is not present in the connection pool, a new connection
804  * handle is opened for the @a service using @a cfg.  Additionally, @a target,
805  * @a connect_notify_cb can be specified to get notified when @a target is
806  * connected at @a service.
807  *
808  * @note @a connect_notify_cb will not be called if @a target is
809  * already connected @a service level. Use
810  * GNUNET_TRANSPORT_check_peer_connected() or a similar function from the
811  * respective @a service's API to check if the target peer is already connected or
812  * not. @a connect_notify_cb will be called only once or never (in case @a target
813  * cannot be connected or is already connected).
814  *
815  * @param peer_id the index of the peer
816  * @param cfg the configuration with which the transport handle has to be
817  *          created if it was not present in the cache
818  * @param service the service of interest
819  * @param cb the callback to notify when the transport handle is available
820  * @param cb_cls the closure for @a cb
821  * @param target the peer identify of the peer whose connection to our TRANSPORT
822  *          subsystem will be notified through the @a connect_notify_cb. Can be NULL
823  * @param connect_notify_cb the callback to call when the @a 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 @a connect_notify_cb
827  * @return the handle which can be used cancel or mark that the handle is no
828  *           longer being used
829  */
830 struct GST_ConnectionPool_GetHandle *
831 GST_connection_pool_get_handle (unsigned int peer_id,
832                                 const struct GNUNET_CONFIGURATION_Handle *cfg,
833                                 enum GST_ConnectionPool_Service service,
834                                 GST_connection_pool_connection_ready_cb cb,
835                                 void *cb_cls,
836                                 const struct GNUNET_PeerIdentity *target,
837                                 GST_connection_pool_peer_connect_notify connect_notify_cb,
838                                 void *connect_notify_cb_cls)
839 {
840   struct GST_ConnectionPool_GetHandle *gh;
841   struct PooledConnection *entry;
842   struct GNUNET_TESTBED_Operation *op;
843   void *handle;
844   uint32_t peer_id32;
845
846   peer_id32 = (uint32_t) peer_id;
847   handle = NULL;
848   entry = NULL;
849   if (NULL != map)
850     entry = GNUNET_CONTAINER_multihashmap32_get (map, peer_id32);
851   if (NULL != entry)
852   {
853     if (entry->in_lru)
854     {
855       GNUNET_assert (0 == entry->demand);
856       expire_task_cancel (entry);
857       GNUNET_CONTAINER_DLL_remove (head_lru, tail_lru, entry);
858       entry->in_lru = GNUNET_NO;
859     }
860     switch (service)
861     {
862     case GST_CONNECTIONPOOL_SERVICE_TRANSPORT:
863       handle = entry->handle_transport;
864       if (NULL != handle)
865         LOG_DEBUG ("Found TRANSPORT handle for peer %u\n",
866                    entry->index);
867       break;
868     case GST_CONNECTIONPOOL_SERVICE_CORE:
869       handle = entry->handle_core;
870       if (NULL != handle)
871         LOG_DEBUG ("Found CORE handle for peer %u\n",
872                    entry->index);
873       break;
874     case GST_CONNECTIONPOOL_SERVICE_ATS_CONNECTIVITY:
875       handle = entry->handle_ats_connectivity;
876       if (NULL != handle)
877         LOG_DEBUG ("Found ATS CONNECTIVITY handle for peer %u\n",
878                    entry->index);
879       break;
880     }
881   }
882   else
883   {
884     entry = GNUNET_new (struct PooledConnection);
885     entry->index = peer_id32;
886     if ((NULL != map)
887         && (GNUNET_CONTAINER_multihashmap32_size (map) < max_size))
888     {
889       GNUNET_assert (GNUNET_OK ==
890                      GNUNET_CONTAINER_multihashmap32_put (map,
891                                                           entry->index,
892                                                           entry,
893                                                           GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST));
894       entry->in_pool = GNUNET_YES;
895     }
896     else
897     {
898       GNUNET_CONTAINER_DLL_insert_tail (head_not_pooled,
899                                         tail_not_pooled,
900                                         entry);
901     }
902     entry->cfg = GNUNET_CONFIGURATION_dup (cfg);
903   }
904   entry->demand++;
905   gh = GNUNET_new (struct GST_ConnectionPool_GetHandle);
906   gh->entry = entry;
907   gh->cb = cb;
908   gh->cb_cls = cb_cls;
909   gh->target = target;
910   gh->connect_notify_cb = connect_notify_cb;
911   gh->connect_notify_cb_cls = connect_notify_cb_cls;
912   gh->service = service;
913   GNUNET_CONTAINER_DLL_insert (entry->head_waiting,
914                                entry->tail_waiting,
915                                gh);
916   if (NULL != handle)
917   {
918     if (NULL == entry->notify_task)
919     {
920       if (NULL != search_waiting (entry, entry->head_waiting))
921         entry->notify_task = GNUNET_SCHEDULER_add_now (&connection_ready,
922                                                        entry);
923     }
924     return gh;
925   }
926   op = NULL;
927   switch (gh->service)
928   {
929   case GST_CONNECTIONPOOL_SERVICE_TRANSPORT:
930     if (NULL != entry->op_transport)
931       return gh;                /* Operation pending */
932     op = GNUNET_TESTBED_operation_create_ (entry,
933                                            &opstart_get_handle_transport,
934                                            &oprelease_get_handle_transport);
935     entry->op_transport = op;
936     break;
937   case GST_CONNECTIONPOOL_SERVICE_CORE:
938     if (NULL != entry->op_core)
939       return gh;                /* Operation pending */
940     op = GNUNET_TESTBED_operation_create_ (entry,
941                                            &opstart_get_handle_core,
942                                            &oprelease_get_handle_core);
943     entry->op_core = op;
944     break;
945   case GST_CONNECTIONPOOL_SERVICE_ATS_CONNECTIVITY:
946     if (NULL != entry->op_ats_connectivity)
947       return gh;                /* Operation pending */
948     op = GNUNET_TESTBED_operation_create_ (entry,
949                                            &opstart_get_handle_ats_connectivity,
950                                            &oprelease_get_handle_ats_connectivity);
951     entry->op_ats_connectivity = op;
952     break;
953   }
954   GNUNET_TESTBED_operation_queue_insert_ (GST_opq_openfds,
955                                           op);
956   GNUNET_TESTBED_operation_begin_wait_ (op);
957   return gh;
958 }
959
960
961 /**
962  * Relinquish a #GST_ConnectionPool_GetHandle object.  If the connection
963  * associated with the object is currently being used by other
964  * #GST_ConnectionPool_GetHandle objects, it is left in the connection pool.  If
965  * no other objects are using the connection and the connection pool is not full
966  * then it is placed in a LRU queue.  If the connection pool is full, then
967  * connections from the LRU queue are evicted and closed to create place for this
968  * connection.  If the connection pool if full and the LRU queue is empty, then
969  * the connection is closed.
970  *
971  * @param gh the handle
972  */
973 void
974 GST_connection_pool_get_handle_done (struct GST_ConnectionPool_GetHandle *gh)
975 {
976   struct PooledConnection *entry;
977
978   if (NULL == gh)
979     return;
980   entry = gh->entry;
981   LOG_DEBUG ("Cleaning up get handle %p for service %u, peer %u\n",
982              gh,
983              gh->service, entry->index);
984   if (! gh->connection_ready_called)
985   {
986     GNUNET_CONTAINER_DLL_remove (entry->head_waiting,
987                                  entry->tail_waiting,
988                                  gh);
989     if ( (NULL == search_waiting (entry, entry->head_waiting)) &&
990          (NULL != entry->notify_task) )
991     {
992       GNUNET_SCHEDULER_cancel (entry->notify_task);
993       entry->notify_task = NULL;
994     }
995   }
996   if (gh->notify_waiting)
997   {
998     GNUNET_CONTAINER_DLL_remove (entry->head_notify,
999                                  entry->tail_notify,
1000                                  gh);
1001     gh->notify_waiting = 0;
1002   }
1003   GNUNET_free (gh);
1004   gh = NULL;
1005   GNUNET_assert (! entry->in_lru);
1006   if (! entry->in_pool)
1007     GNUNET_CONTAINER_DLL_remove (head_not_pooled,
1008                                  tail_not_pooled,
1009                                  entry);
1010   if (NULL != map)
1011   {
1012     if (GNUNET_YES == GNUNET_CONTAINER_multihashmap32_contains (map,
1013                                                                 entry->index))
1014       goto unallocate;
1015     if (GNUNET_CONTAINER_multihashmap32_size (map) == max_size)
1016     {
1017       if (NULL == head_lru)
1018         goto unallocate;
1019       destroy_pooled_connection (head_lru);
1020     }
1021     GNUNET_assert (GNUNET_OK ==
1022                    GNUNET_CONTAINER_multihashmap32_put (map,
1023                                                         entry->index,
1024                                                         entry,
1025                                                         GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1026     entry->in_pool = GNUNET_YES;
1027   }
1028
1029  unallocate:
1030   GNUNET_assert (0 < entry->demand);
1031   entry->demand--;
1032   if (0 != entry->demand)
1033     return;
1034   if (entry->in_pool)
1035   {
1036     add_to_lru (entry);
1037     return;
1038   }
1039   destroy_pooled_connection (entry);
1040 }