Commit a37e5691 authored by xpetrak2's avatar xpetrak2
Browse files

Neighbours enhancement via better LL manipulation

parent 9f9cd40f
......@@ -39,11 +39,87 @@ linkedlist_node_t *linkedlist_append(linkedlist_t *root, void *data)
}
/**
* Delete node from the linked list.
* Apply a function to all nodes of a linked list.
*
* @param node Node to be deleted from the linked list.
* @param root Root of the linked list.
* @param data_func Apply this function to nodes' data. This
* function is called before node_func and
* can be NULL.
* @param node_func Apply this function on the nodes. It can be
* NULL as well.
*/
void linkedlist_apply(linkedlist_t *root,
void (*data_func) (void *data),
void (*node_func) (linkedlist_node_t *node))
{
linkedlist_apply_if(root, NULL, NULL, data_func, node_func);
}
/**
* Apply a function to those linked list nodes and their data, that satisfy
* a predicate. This function degrades into linkedlist_apply() if the
* predicate is NULL.
*
* @param root Root of the linked list.
* @param attribute Predicate's attribute.
* @param pred Choose nodes based on result of this function.
* The predicate succeeds with non-zero result.
* @param data_func Apply this function to selected nodes' data.
* This function is called before node_func and
* can be NULL.
* @param node_func Apply this function on selected nodes. This
* can also be set to NULL.
*/
void linkedlist_apply_if(linkedlist_t *root,
void *attribute,
int (*pred) (void *node_data,
void *attribute),
void (*data_func) (void *data),
void (*node_func) (linkedlist_node_t *node))
{
linkedlist_node_t *current_node;
linkedlist_node_t *next_node;
void *node_data;
current_node = linkedlist_get_first(root);
while (current_node != NULL) {
node_data = current_node->data;
if (pred == NULL || pred(node_data, attribute)) {
if (data_func != NULL) {
data_func(current_node->data);
}
next_node = linkedlist_get_next(root, current_node);
if (node_func != NULL) {
node_func(current_node);
}
}
current_node = next_node;
}
}
/**
* Delete a node from a linked list including its data.
*
* @param node The node to be deleted from the linked list.
*/
void linkedlist_delete(linkedlist_node_t *node)
{
linkedlist_delete_safely(node, NULL);
}
/**
* Delete a node from a linked list including its data and its content.
*
* @param node The node to be deleted from the linked list.
* @param clear_data A clean up function to be applied to node's
* data. If there are no dynamically allocated
* data inside of the node, fill with NULL or
* use linkedlist_delete() instead.
*/
void linkedlist_delete_safely(linkedlist_node_t *node,
void (*clear_data) (void *data))
{
/* fix the links of neighbour nodes */
node->prev->next = node->next;
......@@ -51,6 +127,9 @@ void linkedlist_delete(linkedlist_node_t *node)
/* node's data deletion part */
if (node->data != NULL) {
if (clear_data != NULL) {
clear_data(node->data);
}
free(node->data);
}
......@@ -59,11 +138,15 @@ void linkedlist_delete(linkedlist_node_t *node)
}
/**
* Destroys a linked list.
* Destroys a linked list including data and their content.
*
* @param root Root of the linked list to destroy.
* @param root Root of the linked list to destroy.
* @param clear_data A clean up function to be applied to node's
* data. If there are no dynamically allocated
* data inside of the node, fill with NULL.
*/
void linkedlist_destroy(linkedlist_t *root)
void linkedlist_destroy(linkedlist_t *root,
void (*clear_data) (void *data))
{
linkedlist_node_t *tmp;
......@@ -71,6 +154,9 @@ void linkedlist_destroy(linkedlist_t *root)
while (tmp->next != NULL) {
tmp = tmp->next;
if (tmp->prev->data != NULL) {
if (clear_data != NULL) {
clear_data(tmp->prev->data);
}
free(tmp->prev->data);
}
free(tmp->prev);
......@@ -78,7 +164,18 @@ void linkedlist_destroy(linkedlist_t *root)
}
/**
* Find node in the linked list by data.
* Determine whether a linkedlist has no elements.
*
* @return 1 The linkedlist is empty.
* @return 0 The linkedlist is not empty.
*/
int linkedlist_empty(const linkedlist_t *root)
{
return linkedlist_get_first(root) == NULL;
}
/**
* Find a node in a linked list by data.
*
* @param root Root of the linked list.
* @param data Data of the requested node.
......@@ -86,7 +183,8 @@ void linkedlist_destroy(linkedlist_t *root)
* @return linkedlist_node_t Node containing 'data'.
* @return NULL If the node doesn't exist.
*/
linkedlist_node_t *linkedlist_find(const linkedlist_t *root, const void *data)
linkedlist_node_t *linkedlist_find(const linkedlist_t *root,
const void *data)
{
/* start the search from the first node of the linked list */
linkedlist_node_t *current = linkedlist_get_first(root);
......@@ -251,6 +349,60 @@ linkedlist_node_t *linkedlist_insert_before(linkedlist_t *root,
return linkedlist_insert_after(root, node->prev, data);
}
/**
* Move a node from one linkedlist into another.
*
* @param node Move this node.
* @param dest Into this linked list.
*/
void linkedlist_move(linkedlist_node_t *node, linkedlist_t *dest)
{
linkedlist_node_t *dest_last;
/* remove the node from its LL */
linkedlist_remove(node);
/* insert the node right before the last (stub) node of the dest LL */
dest_last = dest->last.prev;
node->prev = dest_last;
node->next = dest_last->next;
dest_last->next->prev = node;
dest_last->next = node;
}
/**
* Remove a node from a linked list. Keep the node's data intact.
*
* @param node The node to be removed from the linked list.
*/
void linkedlist_remove(linkedlist_node_t *node)
{
/* fix the links of neighbour nodes */
node->prev->next = node->next;
node->next->prev = node->prev;
free(node);
}
/**
* Remove all nodes from a linked list. Keep node's data intact.
*
* @param root The root of the linked list.
*/
void linkedlist_remove_all(linkedlist_t *root)
{
linkedlist_node_t *tmp;
tmp = root->first.next;
while (tmp->next != NULL) {
tmp = tmp->next;
free(tmp->prev);
}
linkedlist_init(root);
}
/**
* Get the number of elements in the linked list.
*
......
......@@ -40,9 +40,26 @@ typedef struct s_linkedlist {
linkedlist_node_t *linkedlist_append(linkedlist_t *root, void *data);
void linkedlist_apply(linkedlist_t *root,
void (*data_func) (void *data),
void (*node_func) (linkedlist_node_t *node));
void linkedlist_apply_if(linkedlist_t *root,
void *attribute,
int (*pred) (void *node_data,
void *attribute),
void (*data_func) (void *data),
void (*node_func) (linkedlist_node_t *node));
void linkedlist_delete(linkedlist_node_t *node);
void linkedlist_destroy(linkedlist_t *root);
void linkedlist_delete_safely(linkedlist_node_t *node,
void (*clear_data) (void *data));
void linkedlist_destroy(linkedlist_t *root,
void (*clear_data) (void *data));
int linkedlist_empty(const linkedlist_t *root);
linkedlist_node_t *linkedlist_find(const linkedlist_t *root, const void *data);
......@@ -66,6 +83,12 @@ linkedlist_node_t *linkedlist_insert_before(linkedlist_t *root,
linkedlist_node_t *node,
void *data);
void linkedlist_move(linkedlist_node_t *node, linkedlist_t *dest);
void linkedlist_remove(linkedlist_node_t *node);
void linkedlist_remove_all(linkedlist_t *root);
size_t linkedlist_size(const linkedlist_t *root);
#endif /* LINKEDLIST_H */
......@@ -53,19 +53,22 @@ neighbour_t *add_new_neighbour(linkedlist_t *neighbours,
}
/* don't add duplicates */
if (find_neighbour_by_addr(neighbours, addr) ||
find_neighbour(neighbours, bev)) {
if (find_neighbour(neighbours, addr, compare_neighbour_addrs) ||
find_neighbour(neighbours, bev, compare_neighbour_bufferevents)) {
free(new_neighbour);
return NULL;
}
/* initialize new neighbour */
memcpy(&new_neighbour->addr, addr, 16);
new_neighbour->failed_pings = 0;
memcpy(&new_neighbour->addr, addr, sizeof(struct in6_addr));
new_neighbour->buffer_event = bev;
new_neighbour->failed_pings = 0;
new_neighbour->flags = 0x0;
new_neighbour->host = NULL;
/* add new neighbour into linked list; NULL if the allocation failed */
if (linkedlist_append(neighbours, new_neighbour) == NULL) {
if (!(new_neighbour->node = linkedlist_append(neighbours,
new_neighbour))) {
clear_neighbour(new_neighbour);
free(new_neighbour);
return NULL;
}
......@@ -74,57 +77,46 @@ neighbour_t *add_new_neighbour(linkedlist_t *neighbours,
}
/**
* Delete all neighbours.
* Safely delete all neighbour's data.
*
* @param neighbours Linked list of our neighbours.
* @param neighbour Delete this neighbour's data.
*/
void clear_neighbours(linkedlist_t *neighbours)
void clear_neighbour(neighbour_t *neighbour)
{
neighbour_t *current;
linkedlist_node_t *current_node;
current_node = linkedlist_get_first(neighbours);
/* safely delete data from the linked list nodes */
while (current_node != NULL) {
/* load current node's data into 'current' */
current = (neighbour_t *) current_node->data;
/* deallocate neighbour's bufferevent */
bufferevent_free(current->buffer_event);
/* get next node in the linked list */
current_node = linkedlist_get_next(neighbours, current_node);
}
/* safely delete all nodes and their data in the linked list */
linkedlist_destroy(neighbours);
bufferevent_free(neighbour->buffer_event);
}
/**
* Delete neighbour from neighbours.
* Comparing function between a neighbour's address and an address.
*
* @param neighbours Linked list of our neighbours.
* @param bev Neighbour's bufferevent.
* @param neighbour Use address of this neighbour.
* @param addr Compare to this address.
*
* @return 0 The addresses equal.
* @return <0 'addr' is greater.
* @return >0 Neighbour's addr is greater.
*/
void delete_neighbour(linkedlist_t *neighbours, struct bufferevent *bev)
int compare_neighbour_addrs(const neighbour_t *neighbour,
const struct in6_addr *addr)
{
neighbour_t *neighbour;
linkedlist_node_t *neighbour_node;
neighbour = find_neighbour(neighbours, bev);
/* no neighbour with bufferevent 'bev' => nothing to delete */
if (neighbour == NULL) {
return;
}
return memcmp(&neighbour->addr, addr, sizeof(neighbour->addr));
}
neighbour_node = linkedlist_find(neighbours, neighbour);
/* since neighbour was found, neighbour_node should never be NULL */
assert(neighbour_node != NULL);
/**
* Comparing function between a neighbour's bufferevent and a bufferevent.
*
* @param neighbour Use bufferevent of this neighbour.
* @param bev Compare to this bufferevent.
*
* @return 0 The bufferevents equal.
* @return 1 The bufferevents do not equal.
*/
int compare_neighbour_bufferevents(const neighbour_t *neighbour,
const struct bufferevent *bev)
{
return neighbour->buffer_event != bev;
}
/* free neighbour's bufferevent */
bufferevent_free(bev);
/* delete the neighbour from the linked list */
linkedlist_delete(neighbour_node);
}
/**
......@@ -176,62 +168,35 @@ int fetch_specific_neighbours(const linkedlist_t *neighbours,
}
/**
* Find neighbour in neighbours based on their bufferevent.
* Find the first neighbour that returns 0 when a comparing function
* is applied on them.
*
* @param neighbours Our neighbours.
* @param bev Neighbour's bufferevent.
* @param attribute Input parameter for the comparing function.
* @param comp_func The comparing function.
*
* @return neighbour_t Requested neighbour.
* @return NULL If not found.
*/
neighbour_t *find_neighbour(const linkedlist_t *neighbours,
const struct bufferevent *bev)
neighbour_t *find_neighbour(const linkedlist_t *neighbours,
const void *attribute,
int (*cmp_func) (const neighbour_t *neighbour,
const void *attribute))
{
/* start the search from the first linked list node */
const linkedlist_node_t *current = linkedlist_get_first(neighbours);
const linkedlist_node_t *current_node;
neighbour_t *current_neighbour;
while (current != NULL) {
/* data of the 'current' node; struct s_neighbour */
neighbour_t *current_data = (neighbour_t *) current->data;
current_node = linkedlist_get_first(neighbours);
while (current_node != NULL) {
current_neighbour = (neighbour_t *) current_node->data;
/* bufferevents equal => neighbour found */
if (current_data->buffer_event == bev) {
/* return node's data; struct s_neighbour */
return current_data;
/* cmp_func returns 0 when current_neighbour's attribute
* is equal to 'attribute' from find_neighbour param */
if (!cmp_func(current_neighbour, attribute)) {
return current_neighbour;
}
/* get next node in the linked list */
current = linkedlist_get_next(neighbours, current);
}
/* neighbour not found */
return NULL;
}
/**
* Find neighbour in neighbours based on their addr.
*
* @param neighbours Our neighbours.
* @param addr Binary ip address stored in 16 bytes.
*
* @return neighbour_t Requested neighbour.
* @return NULL If not found.
*/
neighbour_t *find_neighbour_by_addr(const linkedlist_t *neighbours,
const struct in6_addr *addr)
{
/* start the search from the first linked list node */
const linkedlist_node_t *current = linkedlist_get_first(neighbours);
while (current != NULL) {
/* data of the 'current' node; struct s_neighbour */
neighbour_t *current_data = (neighbour_t *) current->data;
/* ip addresses match => neighbour found */
if (memcmp(&current_data->addr, addr, 16) == 0) {
/* return node's data; struct s_neighbour */
return current_data;
}
/* get next node in the linked list */
current = linkedlist_get_next(neighbours, current);
current_node = linkedlist_get_next(neighbours, current_node);
}
/* neighbour not found */
return NULL;
......
......@@ -23,46 +23,56 @@
#include <netinet/in.h>
#include <stddef.h>
#include "hosts.h"
#include "linkedlist.h"
/* minimum number of peers we need to be connected to */
/** Minimum number of peers we need to be connected to. */
#define MIN_NEIGHBOURS 3
/* request for addresses */
/** Request for addresses. */
#define NEIGHBOUR_ADDRS_REQ 0x01
/** Data type for the linkedlist of neighbours. */
typedef struct s_neighbour {
/** Neighbour's IPv6 address.
* Also allows storing IPv4-mapped IPv6 addresses. */
struct in6_addr addr;
struct in6_addr addr;
/** Bufferevent belonging to this neighbour. */
struct bufferevent *buffer_event;
struct bufferevent *buffer_event;
/** Number of failed ping attempts -- max 3, then disconnect. */
size_t failed_pings;
size_t failed_pings;
/** A set of flags for this neighbour. */
int flags;
int flags;
/** Corresponding host. */
host_t *host;
/** Neighbour's node in the neighbours container. */
linkedlist_node_t *node;
} neighbour_t;
neighbour_t *add_new_neighbour(linkedlist_t *neighbours,
const struct in6_addr *ip_addr,
struct bufferevent *bev);
void clear_neighbours(linkedlist_t *neighbours);
void clear_neighbour(neighbour_t *neighbour);
int compare_neighbour_addrs(const neighbour_t *neighbour,
const struct in6_addr *addr);
int compare_neighbour_bufferevents(const neighbour_t *neighbour,
const struct bufferevent *bev);
void delete_neighbour(linkedlist_t *neighbours, struct bufferevent *bev);
int fetch_specific_neighbours(const linkedlist_t *neighbours,
neighbour_t ***output,
int flags);
neighbour_t *find_neighbour(const linkedlist_t *neighbours,
const struct bufferevent *bev);
neighbour_t *find_neighbour_by_addr(const linkedlist_t *neighbours,
const struct in6_addr *ip_addr);
neighbour_t *find_neighbour(const linkedlist_t *neighbours,
const void *attribute,
int (*cmp_func) (const neighbour_t *neighbour,
const void *attribute));
void set_neighbour_flags(neighbour_t *neighbour, int flags);
void unset_neighbour_flags(neighbour_t *neighbour, int flags);
#endif /* NEIGHBOURS_H */
......@@ -110,8 +110,9 @@ static void p2p_process(struct bufferevent *bev, void *ctx)
global_state = (global_state_t *) ctx;
/* find the neighbour based on their bufferevent */
neighbour = find_neighbour(&global_state->neighbours, bev);
neighbour = find_neighbour(&global_state->neighbours,
bev,
compare_neighbour_bufferevents);
assert(neighbour != NULL);
/* reset neighbour's failed pings */
......@@ -184,7 +185,7 @@ static void timeout_process(linkedlist_t *neighbours,
/* initialize text_ip */
ip_to_string(&neighbour->addr, text_ip);
/* the neighbour hasn't failed enough pings to be deleted */
/* if the neighbour hasn't failed enough pings to be deleted */
if (neighbour->failed_pings < 3) {
/* bufferevent was disabled when timeout flag was set */
bufferevent_enable(neighbour->buffer_event,
......@@ -201,39 +202,10 @@ static void timeout_process(linkedlist_t *neighbours,
neighbour->failed_pings++;
} else {
log_info("%s timed out", text_ip);
delete_neighbour(neighbours, neighbour->buffer_event);
linkedlist_delete_safely(neighbour->node, clear_neighbour);
}
}
/**
* Delete a neighbour from pending neighbours and add it into neighbours.
*
* @param global_state Global state.
* @param neighbour Neighbour to be moved.
*
* @param neighbour_t The new neighbour added into neighbours.
* @param NULL If adding failed.
*/
static neighbour_t *move_neighbour_from_pending(global_state_t *global_state,
neighbour_t *neighbour)
{
linkedlist_node_t *neighbour_node;
neighbour_t *new_neighbour;
new_neighbour = add_new_neighbour(&global_state->neighbours,
&neighbour->addr,
neighbour->buffer_event);
/* if the add was unsuccessful, perform just the full delete */
if (new_neighbour == NULL) {
bufferevent_free(neighbour->buffer_event);
}
neighbour_node = linkedlist_find(&global_state->pending_neighbours,
neighbour);
linkedlist_delete(neighbour_node);
return new_neighbour;
}
/**
* Process the event that occurred on our pending neighbour.
*
......@@ -247,7 +219,6 @@ static void process_pending_neighbour(global_state_t *global_state,
{
int available_hosts_size;
int needed_conns;
neighbour_t *new_neighbour;
char text_ip[INET6_ADDRSTRLEN];
/* fetch hosts with HOST_AVAILABLE flag set;
......@@ -261,21 +232,15 @@ static void process_pending_neighbour(global_state_t *global_state,
/* we've successfully connected to the neighbour */
if (events & BEV_EVENT_CONNECTED) {
log_info("%s successfully connected", text_ip);
/* we've got a new neighbour;
* we can't just delete the neighbour from pending
* and add it into 'neighbours' as the delete would
* free'd the bufferevent
*/
new_neighbour = move_neighbour_from_pending(global_state,
neighbour);
if (new_neighbour == NULL) {
return;
}
/* move the neighbour from pending into neighbours */
linkedlist_move(neighbour->node, &global_state->neighbours);
needed_conns = MIN_NEIGHBOURS -
linkedlist_size(&global_state->neighbours);
/* if we need more neighbours */
if (needed_conns > 0) {
/* and we don't have enough available */
/* and we don't have enough hosts available */
if (available_hosts_size < needed_conns) {
ask_for_addresses(new_neighbour);
}
......@@ -285,8 +250,7 @@ static void process_pending_neighbour(global_state_t *global_state,
log_debug("process_pending_neighbour - connecting to "
"%s was unsuccessful", text_ip);
/* the host is no longer a pending neighbour */
delete_neighbour(&global_state->pending_neighbours,
neighbour->buffer_event);
linkedlist_delete_safely(neighbour->node, clear_neighbour);
}
}
......@@ -308,12 +272,10 @@ static void process_neighbour(global_state_t *global_state,
if (events & BEV_EVENT_ERROR) {
log_info("Connection error, removing %s", text_ip);
delete_neighbour(&global_state->neighbours,
neighbour->buffer_event);
linkedlist_delete_safely(neighbour->node, clear_neighbour