From 94c2a5182bafef9fb8943c02d389723bdbc72752 Mon Sep 17 00:00:00 2001 From: "zugz (tox)" Date: Sat, 14 Mar 2020 00:00:00 +0000 Subject: [PATCH] clear out old conference connections This may fix problems with very large conferences. Sadly, it seems infeasible to test large conferences on one machine, so this is entirely theoretical. --- toxcore/group.c | 101 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 83 insertions(+), 18 deletions(-) diff --git a/toxcore/group.c b/toxcore/group.c index b4248fd8..3e04f181 100644 --- a/toxcore/group.c +++ b/toxcore/group.c @@ -663,28 +663,16 @@ static int addpeer(Group_Chats *g_c, uint32_t groupnumber, const uint8_t *real_p return new_index; } -static bool remove_connection(Group_Chats *g_c, Group_c *g, int friendcon_id) +static void remove_connection(Group_Chats *g_c, Group_c *g, uint16_t i) { - for (uint32_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) { - if (g->connections[i].type == GROUPCHAT_CONNECTION_NONE) { - continue; - } - - if (g->connections[i].number == (unsigned int)friendcon_id) { - if (g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_INTRODUCER) { - --g->num_introducer_connections; - } - - g->connections[i].type = GROUPCHAT_CONNECTION_NONE; - kill_friend_connection(g_c->fr_c, friendcon_id); - return true; - } + if (g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_INTRODUCER) { + --g->num_introducer_connections; } - return false; + kill_friend_connection(g_c->fr_c, g->connections[i].number); + g->connections[i].type = GROUPCHAT_CONNECTION_NONE; } - static void remove_from_closest(Group_c *g, int peer_index) { for (uint32_t i = 0; i < DESIRED_CLOSEST; ++i) { @@ -715,7 +703,16 @@ static bool delpeer(Group_Chats *g_c, uint32_t groupnumber, int peer_index, void const int friendcon_id = getfriend_conn_id_pk(g_c->fr_c, g->group[peer_index].real_pk); if (friendcon_id != -1) { - remove_connection(g_c, g, friendcon_id); + for (uint32_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) { + if (g->connections[i].type == GROUPCHAT_CONNECTION_NONE) { + continue; + } + + if (g->connections[i].number == (unsigned int)friendcon_id) { + remove_connection(g_c, g, i); + break; + } + } } --g->numpeers; @@ -3090,6 +3087,73 @@ static bool groupchat_freeze_timedout(Group_Chats *g_c, uint32_t groupnumber, vo return true; } +/* Push non-empty slots to start. */ +static void squash_connections(Group_c *g) +{ + uint16_t i = 0; + + for (uint16_t j = 0; j < MAX_GROUP_CONNECTIONS; ++j) { + if (g->connections[j].type != GROUPCHAT_CONNECTION_NONE) { + g->connections[i] = g->connections[j]; + ++i; + } + } + + for (; i < MAX_GROUP_CONNECTIONS; ++i) { + g->connections[i].type = GROUPCHAT_CONNECTION_NONE; + } +} + +#define MIN_EMPTY_CONNECTIONS (1 + MAX_GROUP_CONNECTIONS / 10) + +/* Remove old connections as necessary to ensure we have space for new + * connections. This invalidates connections array indices (which is + * why we do this periodically rather than on adding a connection). + */ +static void clean_connections(Group_Chats *g_c, Group_c *g) +{ + uint16_t to_clear = MIN_EMPTY_CONNECTIONS; + + for (uint16_t i = 0; i < MAX_GROUP_CONNECTIONS; ++i) { + if (g->connections[i].type == GROUPCHAT_CONNECTION_NONE) { + --to_clear; + + if (to_clear == 0) { + break; + } + } + } + + for (; to_clear > 0; --to_clear) { + // Remove a connection. Prefer non-closest connections, and given + // that prefer non-online connections, and given that prefer earlier + // slots. + uint16_t i = 0; + + while (i < MAX_GROUP_CONNECTIONS + && (g->connections[i].type != GROUPCHAT_CONNECTION_CONNECTING + || (g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_CLOSEST))) { + ++i; + } + + if (i == MAX_GROUP_CONNECTIONS) { + i = 0; + + while (i < MAX_GROUP_CONNECTIONS - to_clear + && (g->connections[i].type != GROUPCHAT_CONNECTION_ONLINE + || (g->connections[i].reasons & GROUPCHAT_CONNECTION_REASON_CLOSEST))) { + ++i; + } + } + + if (g->connections[i].type != GROUPCHAT_CONNECTION_NONE) { + remove_connection(g_c, g, i); + } + } + + squash_connections(g); +} + /* Send current name (set in messenger) to all online groups. */ void send_name_all_groups(Group_Chats *g_c) @@ -3408,6 +3472,7 @@ void do_groupchats(Group_Chats *g_c, void *userdata) connect_to_closest(g_c, i, userdata); ping_groupchat(g_c, i); groupchat_freeze_timedout(g_c, i, userdata); + clean_connections(g_c, g); if (g->need_send_name) { group_name_send(g_c, i, g_c->m->name, g_c->m->name_length);