+ * @param n the neighbour
+ */
+static void
+trigger_notifications (struct Neighbour *n);
+
+
+/**
+ * Task to call the notification queued in the notifications list of the given
+ * neighbour
+ *
+ * @param cls the neighbour
+ */
+static void
+neighbour_connect_notify_task (void *cls)
+{
+ struct Neighbour *n = cls;
+ struct NeighbourConnectNotification *h;
+
+ GNUNET_assert (NULL != (h = n->nl_head));
+ GNUNET_assert (NULL != n->notify_task);
+ n->notify_task = NULL;
+ GNUNET_assert (NULL != n->controller);
+ GNUNET_CONTAINER_DLL_remove (n->nl_head, n->nl_tail, h);
+ trigger_notifications (n);
+ h->cb (h->cb_cls, n->controller);
+ GNUNET_free (h);
+}
+
+
+/**
+ * Trigger notification task if there are notification requests currently
+ * waiting in the given neighbour. Also activates the neighbour connect operation
+ * if it was previously inactivated so that the connection to the neighbour can
+ * be re-used
+ *
+ * @param n the neighbour
+ */
+static void
+trigger_notifications (struct Neighbour *n)
+{
+ GNUNET_assert (NULL != n->conn_op);
+ if (NULL == n->nl_head)
+ return;
+ if (NULL == n->controller)
+ return;
+ if (NULL != n->notify_task)
+ return;
+ if (1 == n->inactive)
+ {
+ GNUNET_assert (0 == n->reference_cnt);
+ GNUNET_TESTBED_operation_activate_ (n->conn_op);
+ n->inactive = 0;
+ }
+ n->reference_cnt++;
+ n->notify_task =
+ GNUNET_SCHEDULER_add_now (&neighbour_connect_notify_task, n);
+}
+
+
+/**
+ * Callback to be called when the neighbour connect operation is started. The
+ * connection to the neigbour is opened here and any pending notifications are
+ * trigger.
+ *
+ * @param cls the neighbour
+ */
+static void
+opstart_neighbour_conn (void *cls)
+{
+ struct Neighbour *n = cls;
+
+ GNUNET_assert (NULL != n->conn_op);
+ GNUNET_assert (NULL == n->controller);
+ LOG_DEBUG ("Opening connection to controller on host %u\n", n->host_id);
+ n->controller = GNUNET_TESTBED_controller_connect (GST_host_list[n->host_id],
+ EVENT_MASK,
+ &slave_event_cb,
+ NULL);
+ trigger_notifications (n);
+}
+
+
+/**
+ * Callback to be called when the neighbour connect operation is released
+ *
+ * @param cls the neighbour
+ */
+static void
+oprelease_neighbour_conn (void *cls)
+{
+ struct Neighbour *n = cls;
+
+ GNUNET_assert (0 == n->reference_cnt);
+ GNUNET_assert (NULL == n->notify_task);
+ GNUNET_assert (NULL == n->nl_head);
+ if (NULL != n->controller)
+ {
+ LOG_DEBUG ("Closing connection to controller on host %u\n", n->host_id);
+ GNUNET_TESTBED_controller_disconnect (n->controller);
+ n->controller = NULL;
+ }
+ n->conn_op = NULL;
+ n->inactive = 0;
+}
+
+
+/**
+ * Try to open a connection to the given neigbour. If the connection is open
+ * already, then it is re-used. If not, the request is queued in the operation
+ * queues responsible for bounding the total number of file descriptors. The
+ * actual connection will happen when the operation queue marks the
+ * corresponding operation as active.
+ *
+ * @param n the neighbour to open a connection to
+ * @param cb the notification callback to call when the connection is opened
+ * @param cb_cls the closure for the above callback
+ */
+struct NeighbourConnectNotification *
+GST_neighbour_get_connection (struct Neighbour *n,
+ GST_NeigbourConnectNotifyCallback cb,
+ void *cb_cls)
+{
+ struct NeighbourConnectNotification *h;
+
+ GNUNET_assert (NULL != cb);
+ LOG_DEBUG ("Attempting to get connection to controller on host %u\n",
+ n->host_id);
+ h = GNUNET_new (struct NeighbourConnectNotification);
+ h->n = n;
+ h->cb = cb;
+ h->cb_cls = cb_cls;
+ GNUNET_CONTAINER_DLL_insert_tail (n->nl_head, n->nl_tail, h);
+ if (NULL == n->conn_op)
+ {
+ GNUNET_assert (NULL == n->controller);
+ n->conn_op = GNUNET_TESTBED_operation_create_ (n, &opstart_neighbour_conn,
+ &oprelease_neighbour_conn);
+ GNUNET_TESTBED_operation_queue_insert_ (GST_opq_openfds, n->conn_op);
+ GNUNET_TESTBED_operation_begin_wait_ (n->conn_op);
+ return h;
+ }
+ trigger_notifications (n);
+ return h;
+}
+
+
+/**
+ * Cancel the request for opening a connection to the neighbour
+ *
+ * @param h the notification handle
+ */
+void
+GST_neighbour_get_connection_cancel (struct NeighbourConnectNotification *h)
+{
+ struct Neighbour *n;
+ int cleanup_task;
+
+ n = h->n;
+ cleanup_task = (h == n->nl_head) ? GNUNET_YES : GNUNET_NO;
+ GNUNET_CONTAINER_DLL_remove (n->nl_head, n->nl_tail, h);
+ GNUNET_free (h);
+ if (GNUNET_NO == cleanup_task)
+ return;
+ if (NULL == n->notify_task)
+ return;
+ GNUNET_assert (0 < n->reference_cnt);
+ n->reference_cnt--;
+ GNUNET_SCHEDULER_cancel (n->notify_task);
+ n->notify_task = NULL;
+ if (NULL == n->nl_head)
+ {
+ if ((0 == n->reference_cnt) && (0 == n->inactive))
+ {
+ n->inactive = 1;
+ GNUNET_TESTBED_operation_inactivate_ (n->conn_op);
+ }
+ return;
+ }
+ trigger_notifications (n);
+}
+
+
+/**
+ * Release the connection to the neighbour. The actual connection will be
+ * closed if connections to other neighbour are waiting (to maintain a bound on
+ * the total number of connections that are open).
+ *
+ * @param n the neighbour whose connection can be closed
+ */
+void
+GST_neighbour_release_connection (struct Neighbour *n)
+{
+ GNUNET_assert (0 == n->inactive);
+ GNUNET_assert (0 < n->reference_cnt);
+ n->reference_cnt--;
+ if (0 == n->reference_cnt)
+ {
+ n->inactive = 1;
+ GNUNET_TESTBED_operation_inactivate_ (n->conn_op);
+ }
+}
+
+
+/**
+ * Cleanup neighbour connect contexts
+ *
+ * @param ncc the neighbour connect context to cleanup
+ */
+static void
+cleanup_ncc (struct NeighbourConnectCtxt *ncc)
+{
+ if (NULL != ncc->nh)
+ GST_neighbour_get_connection_cancel (ncc->nh);
+ if (NULL != ncc->timeout_task)
+ GNUNET_SCHEDULER_cancel (ncc->timeout_task);
+ GNUNET_CONTAINER_DLL_remove (ncc_head,
+ ncc_tail,
+ ncc);
+ GNUNET_free (ncc);
+}
+
+
+/**
+ * Cleans up the neighbour list
+ */
+void
+GST_neighbour_list_clean ()
+{
+ struct Neighbour *n;
+ unsigned int id;
+
+ for (id = 0; id < neighbour_list_size; id++)
+ {
+ if (NULL == (n = neighbour_list[id]))
+ continue;
+ if (NULL != n->conn_op)
+ GNUNET_TESTBED_operation_release_ (n->conn_op);
+ GNUNET_free (n);
+ neighbour_list[id] = NULL;
+ }
+ GNUNET_free_non_null (neighbour_list);
+}
+
+
+/**
+ * Get a neighbour from the neighbour list
+ *
+ * @param id the index of the neighbour in the neighbour list
+ * @return the Neighbour; NULL if the given index in invalid (index greater than
+ * the list size or neighbour at that index is NULL)
+ */
+struct Neighbour *
+GST_get_neighbour (uint32_t id)
+{
+ if (neighbour_list_size <= id)
+ return NULL;
+ return neighbour_list[id];
+}
+
+
+/**
+ * Function to cleanup the neighbour connect contexts