+GAVL_CUST_NODE_INT_DEC(forb_peer_nolock,/* cust_prefix */
+ forb_t, /* cust_root_t */
+ forb_peer_t, /* cust_item_t */
+ forb_server_id, /* cust_key_t */
+ peers, /* cust_root_node */
+ gnode, /* cust_item_node */
+ server_id, /* cust_item_key */
+ forb_server_id_cmp)/* cust_cmp_fnc */
+
+GAVL_CUST_NODE_INT_IMP(forb_peer_nolock,/* cust_prefix */
+ forb_t, /* cust_root_t */
+ forb_peer_t, /* cust_item_t */
+ forb_server_id, /* cust_key_t */
+ peers, /* cust_root_node */
+ gnode, /* cust_item_node */
+ server_id, /* cust_item_key */
+ forb_server_id_cmp);/* cust_cmp_fnc */
+
+int forb_discovery_init(forb_t *forb)
+{
+ if (fosa_mutex_init(&forb->peer_mutex, 0) != 0) return -1;
+ forb_peer_nolock_init_root_field(forb);
+
+ if (fosa_mutex_init(&forb->objkey_mutex, 0) != 0) return -1;
+ forb_objects_nolock_init_root_field(forb);
+ return 0;
+}
+
+static inline void
+forb_peer_insert(forb_t *forb, forb_peer_t *peer)
+{
+ fosa_mutex_lock(&forb->peer_mutex);
+ forb_peer_get(peer);
+ forb_peer_nolock_insert(forb, peer);
+ fosa_mutex_unlock(&forb->peer_mutex);
+}
+
+static inline void
+forb_peer_delete(forb_t *forb, forb_peer_t *peer)
+{
+ fosa_mutex_lock(&forb->peer_mutex);
+ forb_peer_nolock_delete(forb, peer);
+ forb_peer_put(peer);
+ fosa_mutex_unlock(&forb->peer_mutex);
+}
+
+/**
+ * Finds peer with given @a server_id.
+ *
+ * @param forb
+ * @param server_id
+ *
+ * @return The found peer or NULL if no peer is found. You have to
+ * call forb_peer_put() after you finish working with the non NULL
+ * retuned value.
+ */
+forb_peer_t *
+forb_peer_find(forb_t *forb, forb_server_id *server_id)
+{
+ forb_peer_t *ret;
+ fosa_mutex_lock(&forb->peer_mutex);
+ ret = forb_peer_nolock_find(forb, server_id);
+ if (ret) {
+ forb_peer_get(ret);
+ }
+ fosa_mutex_unlock(&forb->peer_mutex);
+ return ret;
+}
+
+/**
+ * Finds the peer with given @a server_id. If the peer is not
+ * currently known (not yet discovered or not available), this
+ * function waits at maximum @a timeout for the peer to be discovered.
+ *
+ * @param forb
+ * @param server_id
+ * @param timeout
+ *
+ * @return The peer structure or NULL if the peer is not known even
+ * after the timeout elapses.
+ *
+ * @note After the returned peer is not needed, forb_peer_put() must
+ * called on it.
+ */
+forb_peer_t *
+forb_peer_find_timed(forb_t *forb, forb_server_id *server_id,
+ fosa_abs_time_t *timeout)
+{
+ forb_peer_t *peer;
+ bool peer_allocated = false;
+
+ fosa_mutex_lock(&forb->peer_mutex);
+ peer = forb_peer_nolock_find(forb, server_id);
+ if (!peer && !timeout) goto unlock;
+ if (peer) {
+ forb_peer_get(peer);
+ } else {
+ /* We are the first who want to contact this peer */
+ peer = forb_peer_new();
+ if (!peer) goto unlock;
+ peer->server_id = *server_id;
+ peer->state = FORB_PEER_WANTED;
+ forb_peer_nolock_insert(forb, forb_peer_get(peer));
+ peer_allocated = true;
+ }
+ while (peer->state == FORB_PEER_WANTED) {
+ int ret;
+ /* Wait for the peer to be discovered. */
+ ret = fosa_cond_timedwait(&peer->cond,
+ &forb->peer_mutex,
+ timeout);
+ if (ret == FOSA_ETIMEDOUT) {
+ /* No peer discovered within timeout */
+ if (peer_allocated) {
+ forb_peer_nolock_delete(forb, peer);
+ forb_peer_put(peer);
+ }
+ forb_peer_put(peer);
+ peer = NULL;
+ break;
+ }
+ }
+unlock:
+ fosa_mutex_unlock(&forb->peer_mutex);
+
+ return peer;
+}
+
+/**
+ *
+ * @param port
+ * @param peer
+ * @param server_id
+ * @param addr
+ * @param orb_id
+ *
+ * @warning This function has to be called either from receiver thread
+ * of when the recevier thread is not running.
+ */