From 08e22453a438c8a3f6135632a6ce39239b47d9f5 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 10 Dec 2018 14:56:34 +0100 Subject: more work on simple plugin --- src/ats/plugin_ats2_simple.c | 271 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 247 insertions(+), 24 deletions(-) diff --git a/src/ats/plugin_ats2_simple.c b/src/ats/plugin_ats2_simple.c index 689524ba4..55234f1bc 100644 --- a/src/ats/plugin_ats2_simple.c +++ b/src/ats/plugin_ats2_simple.c @@ -23,7 +23,7 @@ * * TODO: * - subscribe to PEERSTORE when short on HELLOs (given application preferences!) - * - keep track of HELLOs and when we tried them last => re-suggest + * - keep track of HELLOs and when we tried them last => re-suggest * - sum up preferences per peer, keep totals! => PeerMap pid -> [preferences + sessions + addrs!] * - sum up preferences overall, keep global sum => starting point for "proportional" * - store DLL of available sessions per peer @@ -35,6 +35,12 @@ #define LOG(kind,...) GNUNET_log_from (kind, "ats-simple",__VA_ARGS__) +/** + * A handle for the proportional solver + */ +struct SimpleHandle; + + /** * Entry in list of addresses we could try per peer. */ @@ -50,7 +56,7 @@ struct Hello * Kept in a DLL. */ struct Hello *prev; - + /** * The address we could try. */ @@ -75,6 +81,13 @@ struct Hello }; +/** + * Information about preferences and sessions we track + * per peer. + */ +struct Peer; + + /** * Internal representation of a session by the plugin. * (If desired, plugin may just use NULL.) @@ -91,7 +104,7 @@ struct GNUNET_ATS_SessionHandle * Kept in DLL per peer. */ struct GNUNET_ATS_SessionHandle *prev; - + /** * The session in the main ATS service. */ @@ -106,7 +119,12 @@ struct GNUNET_ATS_SessionHandle * Hello matching this session, or NULL for none. */ struct Hello *hello; - + + /** + * Peer this session is for. + */ + struct Peer *peer; + /** * Address used by this session (largely for debugging). */ @@ -142,6 +160,21 @@ struct Peer */ struct GNUNET_ATS_SessionHandle *sh_tail; + /** + * Kept in a DLL. + */ + struct Hello *h_head; + + /** + * Kept in a DLL. + */ + struct Hello *h_tail; + + /** + * The handle for the proportional solver + */ + struct SimpleHandle *h; + /** * Which peer is this for? */ @@ -149,7 +182,7 @@ struct Peer /** * Array where we sum up the bandwidth requests received indexed - * by preference kind (see `struct GNUNET_MQ_PreferenceKind`) + * by preference kind (see `enum GNUNET_MQ_PreferenceKind`) */ uint64_t bw_by_pk[GNUNET_MQ_PREFERENCE_COUNT]; @@ -162,8 +195,8 @@ struct Peer /** * Task used to try again to suggest an address for this peer. */ - struct GNUNET_SCHEDULER_TaskHandle *task; - + struct GNUNET_SCHEDULER_Task *task; + }; @@ -216,10 +249,146 @@ struct SimpleHandle * Handle to the peerstore service. */ struct GNUNET_PEERSTORE_Handle *ps; - + }; +/** + * Lookup peer in the peers map. + * + * @param h handle to look up in + * @param pid peer identity to look up by + * @return NULL for not found + */ +struct Peer * +lookup_peer (struct SimpleHandle *h, + const struct GNUNET_PeerIdentity *pid) +{ + return GNUNET_CONTAINER_multipeermap_get (h->peers, + pid); +} + + +/** + * Check if there is _any_ interesting information left we + * store about the peer in @a p. + * + * @param p peer to test if we can drop the data structure + * @return #GNUNET_YES if no information is left in @a p + */ +static int +peer_test_dead (struct Peer *p) +{ + for (enum GNUNET_MQ_PreferenceKind pk = 0; + pk < GNUNET_MQ_PREFERENCE_COUNT; + pk++) + if (0 != p->bw_by_pk[pk]) + return GNUNET_NO; + if (NULL != p->sh_head) + return GNUNET_NO; + return GNUNET_YES; +} + + +/** + * Function called by PEERSTORE for each matching record. + * + * @param cls closure with a `struct Peer` + * @param record peerstore record information + * @param emsg error message, or NULL if no errors + */ +static void +watch_cb (void *cls, + const struct GNUNET_PEERSTORE_Record *record, + const char *emsg) +{ + struct Peer *p = cls; + + // FIXME: process hello! + // check for expiration + // (add to p's doubly-linked list) + + if (NULL == p->task) + { + // start suggestion task! + } +} + + +/** + * Find or add peer if necessary. + * + * @param h our plugin handle + * @param pid the peer identity to add/look for + * @return a peer handle + */ +static struct Peer * +peer_add (struct SimpleHandle *h, + const struct GNUNET_PeerIdentity *pid) +{ + struct Peer *p = lookup_peer (h, + pid); + + if (NULL != p) + return p; + p = GNUNET_new (struct Peer); + p->h = h; + p->pid = *pid; + p->wc = GNUNET_PEERSTORE_watch (h->ps, + "transport", + &p->pid, + "HELLO" /* key */, + &watch_cb, + p); + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multipeermap_put (h->peers, + &p->pid, + p, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + + return p; +} + + +/** + * Free the entry (and associated tasks) of peer @a p. + * Note that @a p must be dead already (see #peer_test_dead()). + * + * @param p the peer to free + */ +static void +peer_free (struct Peer *p) +{ + struct SimpleHandle *h = p->h; + struct Hello *hello; + + GNUNET_assert (NULL == p->sh_head); + while (NULL != (hello = p->h_head)) + { + GNUNET_CONTAINER_DLL_remove (p->h_head, + p->h_tail, + hello); + GNUNET_assert (NULL == hello->sh); + GNUNET_free (hello); + } + if (NULL != p->task) + { + GNUNET_SCHEDULER_cancel (p->task); + p->task = NULL; + } + if (NULL != p->wc) + { + GNUNET_PEERSTORE_watch_cancel (p->wc); + p->wc = NULL; + } + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multipeermap_remove (h->peers, + &p->pid, + p)); + GNUNET_free (p); +} + + /** * The world changed, recalculate our allocations. */ @@ -243,9 +412,12 @@ simple_preference_add (void *cls, const struct GNUNET_ATS_Preference *pref) { struct SimpleHandle *h = cls; - // Setup peer if necessary (-> including HELLO triggers!) - // add pref to bw_by_pk - // trigger update + struct Peer *p = peer_add (h, + &pref->peer); + + GNUNET_assert (pref->pk < GNUNET_MQ_PREFERENCE_COUNT); + p->bw_by_pk[pref->pk] += ntohl (pref->bw.value__); + update (h); return NULL; } @@ -254,20 +426,26 @@ simple_preference_add (void *cls, * The plugin should end respecting a preference. * * @param cls the closure - * @param ph whatever @e preference_add returned + * @param ph whatever @e preference_add returned * @param pref the preference to delete * @return plugin's internal representation, or NULL */ static void -simple_preference_del (void *cls, +simple_preference_del (void *cls, struct GNUNET_ATS_PreferenceHandle *ph, const struct GNUNET_ATS_Preference *pref) { struct SimpleHandle *h = cls; - // find peer - // subtract pref from bw_by_pk - // remove peer if otherwise dead - // trigger update + struct Peer *p = lookup_peer (h, + &pref->peer); + + GNUNET_assert (NULL != p); + GNUNET_assert (pref->pk < GNUNET_MQ_PREFERENCE_COUNT); + p->bw_by_pk[pref->pk] -= ntohl (pref->bw.value__); + if ( (0 == p->bw_by_pk[pref->pk]) && + (GNUNET_YES == peer_test_dead (p)) ) + peer_free (p); + update (h); } @@ -286,11 +464,47 @@ simple_session_add (void *cls, const char *address) { struct SimpleHandle *h = cls; + struct Peer *p = peer_add (h, + &data->peer); + struct Hello *hello; + size_t alen; + struct GNUNET_ATS_SessionHandle *sh; - // find or add peer if necessary - // setup session - // match HELLO - // trigger update + /* setup session handle */ + if (NULL == address) + alen = 0; + else + alen = strlen (address) + 1; + sh = GNUNET_malloc (sizeof (struct GNUNET_ATS_SessionHandle) + alen); + sh->peer = p; + sh->session = data->session; + sh->data = data; + if (NULL == address) + { + sh->address = NULL; + } + else + { + memcpy (&sh[1], + address, + alen); + sh->address = (const char *) &sh[1]; + } + GNUNET_CONTAINER_DLL_insert (p->sh_head, + p->sh_tail, + sh); + /* match HELLO */ + hello = p->h_head; + while ( (NULL != hello) && + (0 != strcmp (address, + hello->address)) ) + hello = hello->next; + if (NULL != hello) + { + hello->sh = sh; + sh->hello = hello; + } + update (h); return NULL; } @@ -309,7 +523,9 @@ simple_session_update (void *cls, const struct GNUNET_ATS_SessionData *data) { struct SimpleHandle *h = cls; - // trigger update + + sh->data = data; /* this statement should not really do anything... */ + update (h); } @@ -326,9 +542,16 @@ simple_session_del (void *cls, const struct GNUNET_ATS_SessionData *data) { struct SimpleHandle *h = cls; - // tear down session + struct Peer *p = sh->peer; + + // FIXME: tear down session // del peer if otherwise dead - // trigger update + + + if ( (NULL == p->sh_head) && + (GNUNET_YES == peer_test_dead (p)) ) + peer_free (p); + update (h); } -- cgit v1.2.3