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