/** * \brief This function unsubscribe client from the layer */ int vs_layer_unsubscribe(struct VSLayer *layer, struct VSession *vsession) { struct VSEntitySubscriber *layer_subscriber; /* Try to find layer subscriber */ layer_subscriber = layer->layer_subs.first; while(layer_subscriber != NULL) { if(layer_subscriber->node_sub->session->session_id == vsession->session_id) { break; } layer_subscriber = layer_subscriber->next; } /* Client has to be subscribed to the layer */ if(layer_subscriber == NULL) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s() client not subscribed to the layer (id: %d)\n", __FUNCTION__, layer->id); return 0; } /* Remove client from the list of subscribers */ v_list_free_item(&layer->layer_subs, layer_subscriber); return 1; }
/** * \brief This function is called, when server receive ack command of packet * that contained node_destroy command that was sent to the client * * When this function is called, then we can be sure, that client knows, that * this node was destroyed. When the last one of follower acknowledge receiving * of this command, then this node could be deleted */ int vs_handle_node_destroy_ack(struct VS_CTX *vs_ctx, struct VSession *vsession, struct Generic_Cmd *cmd) { struct VSNode *node; struct VSEntityFollower *node_follower, *next_node_follower; struct Node_Destroy_Ack_Cmd *node_destroy_ack = (struct Node_Destroy_Ack_Cmd*)cmd; /* Try to find node */ if((node = vs_node_find(vs_ctx, node_destroy_ack->node_id)) == NULL) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s() node (id: %d) not found\n", __FUNCTION__, node_destroy_ack->node_id); return 0; } /* Remove corresponding follower from the list of followers */ node_follower = node->node_folls.first; while(node_follower != NULL) { next_node_follower = node_follower->next; if(node_follower->node_sub->session->session_id == vsession->session_id) { node_follower->state = ENTITY_DELETED; v_list_free_item(&node->node_folls, node_follower); break; } node_follower = next_node_follower; } /* When node doesn't have any follower, then it is possible to destroy this node*/ if(node->node_folls.first == NULL) { node->state = ENTITY_DELETED; vs_node_destroy(vs_ctx, node); } return 1; }
/** * \brief This function will try to remove node from the server. The node can't * have any child node or subscriber. */ static int vs_node_destroy(struct VS_CTX *vs_ctx, struct VSNode *node) { /* Node can't have any followers. The VSNode can be destroyed, when server * receive ack of node_destroy command from all clients */ if(node->node_folls.first == NULL) { /* Node can't have any child node */ if(node->children_links.first == NULL) { /* Remove node permissions */ if(node->permissions.first != NULL) { v_list_free(&node->permissions); } /* Remove link on this node from parent node */ if(node->parent_link != NULL) { struct VSNode *parent_node = node->parent_link->parent; v_list_free_item(&parent_node->children_links, node->parent_link); } /* Remove all tag groups and tags */ if(node->tag_groups.lb.first != NULL) { vs_node_taggroups_destroy(node); } v_hash_array_destroy(&node->tag_groups); /* Remove all layers */ if(node->layers.lb.first != NULL) { vs_node_layers_destroy(node); } v_hash_array_destroy(&node->layers); v_print_log(VRS_PRINT_DEBUG_MSG, "Node: %d destroyed\n", node->id); /* Remove node from the hashed linked list of nodes */ v_hash_array_remove_item(&vs_ctx->data.nodes, node); free(node); return 1; } else { /* This should never happen */ v_print_log(VRS_PRINT_DEBUG_MSG, "%s(): node (id: %d) with child nodes can't be destroyed\n", __FUNCTION__, node->id); return 0; } } else { /* This should never happen */ v_print_log(VRS_PRINT_WARNING, "%(): node (id: %d) with followers can't be destroyed\n", __FUNCTION__, node->id); return 0; } }
/** * \brief This function unsubscribes client from the tag group */ int vs_taggroup_unsubscribe(struct VSTagGroup *tg, struct VSession *vsession) { struct VSEntitySubscriber *tg_subscriber; /* If client is subscribed to this tag group, then remove this client * from list of subscribers */ tg_subscriber = tg->tg_subs.first; while(tg_subscriber != NULL) { if(tg_subscriber->node_sub->session == vsession) { VSTag *tag; VBucket *bucket; struct VSEntityFollower *tag_follower; /* Go through all tags in this tag group */ bucket = tg->tags.lb.first; while(bucket != NULL) { tag = (struct VSTag*)bucket->data; tag_follower = tag->tag_folls.first; /* Remove client from list of tag followers */ while(tag_follower != NULL) { if(tag_follower->node_sub->session == vsession) { v_list_free_item(&tag->tag_folls, tag_follower); break; } tag_follower = tag_follower->next; } bucket = bucket->next; } /* Remove client from list of tag group subscribers */ v_list_free_item(&tg->tg_subs, tg_subscriber); return 1; } tg_subscriber = tg_subscriber->next; } return 0; }
int vs_handle_taggroup_destroy_ack(struct VS_CTX *vs_ctx, struct VSession *vsession, struct Generic_Cmd *cmd) { struct VSNode *node; struct VSTagGroup *tg; struct VSEntityFollower *tg_foll, *next_tg_foll; struct TagGroup_Destroy_Ack_Cmd *cmd_tg_destroy_ack = (struct TagGroup_Destroy_Ack_Cmd*)cmd; /* Try to find node */ if((node = vs_node_find(vs_ctx, cmd_tg_destroy_ack->node_id)) == NULL) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s() node (id: %d) not found\n", __FUNCTION__, cmd_tg_destroy_ack->node_id); return 0; } if( (tg = vs_taggroup_find(node, cmd_tg_destroy_ack->taggroup_id)) == NULL) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s() tag_group (id: %d) in node (id: %d) not found\n", __FUNCTION__, cmd_tg_destroy_ack->taggroup_id, cmd_tg_destroy_ack->node_id); return 0; } tg_foll = tg->tg_folls.first; while(tg_foll != NULL) { next_tg_foll = tg_foll->next; if(tg_foll->node_sub->session->session_id == vsession->session_id) { tg_foll->state = ENTITY_DELETED; v_list_free_item(&tg->tg_folls, tg_foll); } tg_foll = next_tg_foll; } /* When taggroup doesn't have any follower, then it is possible to destroy * this taggroup */ if(tg->tg_folls.first == NULL) { tg->state = ENTITY_DELETED; vs_taggroup_destroy(node, tg); } return 1; }
int vs_handle_layer_destroy_ack(struct VS_CTX *vs_ctx, struct VSession *vsession, struct Generic_Cmd *cmd) { struct VSNode *node; struct VSLayer *layer; struct VSEntityFollower *layer_foll, *next_layer_foll; struct Layer_Destroy_Ack_Cmd *layer_destroy_cmd = (struct Layer_Destroy_Ack_Cmd*)cmd; /* Try to find node */ if((node = vs_node_find(vs_ctx, layer_destroy_cmd->node_id)) == NULL) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s() node (id: %d) not found\n", __FUNCTION__, layer_destroy_cmd->node_id); return 0; } /* Try to find layer */ if( (layer = vs_layer_find(node, layer_destroy_cmd->layer_id)) == NULL) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s() layer (id: %d) in node (id: %d) not found\n", __FUNCTION__, layer_destroy_cmd->layer_id, layer_destroy_cmd->node_id); return 0; } /* Mark the layer in this session as DELETED and remove this follower from * the list of layer followers */ layer_foll = layer->layer_folls.first; while(layer_foll != NULL) { next_layer_foll = layer_foll->next; if(layer_foll->node_sub->session->session_id == vsession->session_id) { layer_foll->state = ENTITY_DELETED; v_list_free_item(&layer->layer_folls, layer_foll); } layer_foll = next_layer_foll; } /* When layer doesn't have any follower, then it is possible to destroy * this layer */ if(layer->layer_folls.first == NULL) { layer->state = ENTITY_DELETED; vs_layer_destroy(node, layer); } return 0; }
/** * \brief This function do recursive unsubscribing from node */ static int vs_node_unsubscribe(struct VSNode *node, struct VSNodeSubscriber *node_subscriber, int level) { struct VSNode *child_node; struct VSLink *link; struct VBucket *tg_bucket, *layer_bucket; struct VSTagGroup *tg; struct VSLayer *layer; struct VSNodeSubscriber *_node_subscriber, *_next_node_subscriber; struct VSEntityFollower *node_follower; struct VSEntityFollower *taggroup_follower; struct VSEntityFollower *layer_follower; /* Unsubscribe from all child nodes */ link = node->children_links.first; while(link != NULL) { child_node = link->child; _node_subscriber = child_node->node_subs.first; while(_node_subscriber != NULL) { _next_node_subscriber = _node_subscriber->next; if(_node_subscriber->session->session_id == node_subscriber->session->session_id) { /* Unsubscribe from child node */ vs_node_unsubscribe(child_node, _node_subscriber, level+1); break; } _node_subscriber = _next_node_subscriber; } link = link->next; } /* Unsubscribe client from all tag groups */ tg_bucket = node->tag_groups.lb.first; while(tg_bucket != NULL) { tg = (struct VSTagGroup*)tg_bucket->data; /* Remove client from the list of TagGroup subscribers */ vs_taggroup_unsubscribe(tg, node_subscriber->session); /* Remove client from the list of TagGroup followers */ taggroup_follower = tg->tg_folls.first; while(taggroup_follower != NULL) { if(taggroup_follower->node_sub->session->session_id == node_subscriber->session->session_id) { v_list_free_item(&tg->tg_folls, taggroup_follower); break; } taggroup_follower = taggroup_follower->next; } tg_bucket = tg_bucket->next; } /* Unsubscribe client from all layers */ layer_bucket = node->layers.lb.first; while(layer_bucket != NULL) { layer = (struct VSLayer*)layer_bucket->data; /* Remove client from the list of Layer subscribers */ vs_layer_unsubscribe(layer, node_subscriber->session); /* Remove client from the list of Layer followers */ layer_follower = layer->layer_folls.first; while(layer_follower != NULL) { if(layer_follower->node_sub->session->session_id == node_subscriber->session->session_id) { v_list_free_item(&layer->layer_folls, layer_follower); break; } layer_follower = layer_follower->next; } layer_bucket = layer_bucket->next; } if(level > 0) { /* Remove this session from list of followers too */ node_follower = node->node_folls.first; while(node_follower != NULL) { if(node_follower->node_sub->session->session_id == node_subscriber->session->session_id) { /* Remove client from list of clients, that knows about this node */ v_print_log(VRS_PRINT_DEBUG_MSG, "Removing session: %d from the list of node: %d followers\n", node_subscriber->session->session_id, node->id); v_list_free_item(&node->node_folls, node_follower); break; } node_follower = node_follower->next; } } /* Finally remove this session from list of node subscribers */ v_list_free_item(&node->node_subs, node_subscriber); return 1; }
/** * \brief This function creates new VSNode at Verse server */ struct VSNode *vs_node_create(struct VS_CTX *vs_ctx, struct VSNode *parent_node, struct VSUser *owner, uint32 node_id, uint16 custom_type) { struct VSNode *node; struct VSLink *link; struct VBucket *bucket; if(! (v_hash_array_count_items(&vs_ctx->data.nodes) < VRS_MAX_COMMON_NODE_COUNT) ) { v_print_log(VRS_PRINT_DEBUG_MSG, "max number: %d of nodes reached\n", VRS_MAX_COMMON_NODE_COUNT); return NULL; } node = (struct VSNode*)calloc(1, sizeof(struct VSNode)); if(node == NULL) { v_print_log(VRS_PRINT_ERROR, "Out of memory\n"); return NULL; } vs_node_init(node); if(node_id == VRS_RESERVED_NODE_ID) { /* Try to find first free node_id. It is fast and easy, When * VRS_LAST_COMMON_NODE_ID did not reach 0xFFFFFFFF-1 value yet and not used * node_id are not reused.*/ node->id = vs_ctx->data.last_common_node_id + 1; while( v_hash_array_find_item(&vs_ctx->data.nodes, &node) != NULL) { node->id++; /* Node id 0xFFFFFFFF has special purpose and node IDs in range <0, 65535> * have special purposes too (skip them) */ if(node->id > VRS_LAST_COMMON_NODE_ID) node->id = VRS_FIRST_COMMON_NODE_ID; /* TODO: implement faster finding of free node id */ } vs_ctx->data.last_common_node_id = node->id; } else { node->id = node_id; } /* Create link to the parent node */ if(parent_node != NULL) { link = vs_link_create(parent_node, node); if(link == NULL) { v_print_log(VRS_PRINT_DEBUG_MSG, "link between nodes %d %d could not be created\n", parent_node->id, node->id); free(node); return NULL; } } else { /* This can happen only for root node */ assert(node_id == VRS_ROOT_NODE_ID); node->parent_link = NULL; node->level = 0; } /* Add node to the hashed array of all verse nodes */ bucket = v_hash_array_add_item(&vs_ctx->data.nodes, node, sizeof(struct VSNode)); if(bucket == NULL) { v_print_log(VRS_PRINT_DEBUG_MSG, "node %d could not be added to the hashed list of nodes\n", node->id); if(node->parent_link != NULL) { v_list_free_item(&parent_node->children_links, node->parent_link); } free(node); return NULL; } node->owner = owner; node->type = custom_type; return node; }
/** * \brief This function is called, when acknowledgment packet was received. * * This function processes all ACK and NAK commands and it add not obsolete * commands from the history of sent packets to the out queue again. This * function also removes positively and negatively acknowledged packets from * history of sent packets. ANK ID is updated. * * \param[in] *C The verse context. * * \return This function returns index of last ACK command in sequence of * system command, when ACK and NAK commands are the beginning of system * commands, otherwise it returns -2. When no ACK or NAK command was found, * then -1 is returned; */ static int handle_ack_nak_commands(struct vContext *C) { struct VSession *vsession = CTX_current_session(C); struct VDgramConn *vconn = CTX_current_dgram_conn(C); struct VPacket *r_packet = CTX_r_packet(C); struct VSent_Packet *sent_packet; struct VSent_Command *sent_cmd, *sent_cmd_prev; unsigned long int rtt = ULONG_MAX; struct timeval tv; uint32 ack_id, nak_id; int i, ret=-1; gettimeofday(&tv, NULL); /* Compute SRTT */ if(r_packet->sys_cmd[0].cmd.id==CMD_ACK_ID) { unsigned long int tmp; int i=0; /* Try to find the smallest RTT from acknowledged packets */ for(i=0; r_packet->sys_cmd[i].cmd.id!=CMD_RESERVED_ID; i++) { if(r_packet->sys_cmd[i].cmd.id==CMD_ACK_ID) { sent_packet = v_packet_history_find_packet(&vconn->packet_history, r_packet->sys_cmd[i].ack_cmd.pay_id); if(sent_packet!=NULL) { tmp = packet_rtt(sent_packet, &tv); if(tmp<rtt) rtt=tmp; } } } if(rtt<ULONG_MAX) { /* Computation of SRTT as described in RFC */ if(vconn->srtt==0) { vconn->srtt = rtt; } else { vconn->srtt = RTT_ALPHA*vconn->srtt + (1-RTT_ALPHA)*rtt; } v_print_log(VRS_PRINT_DEBUG_MSG, "RTT: %d [us]\n", rtt); v_print_log(VRS_PRINT_DEBUG_MSG, "SRTT: %d [us]\n", vconn->srtt); } } /* Process all ACK and NAK commands. ACK and NAK commands should be first * and there should not be other system commands between ACK and NAK * commands. */ for(i=0; r_packet->sys_cmd[i].cmd.id == CMD_NAK_ID || r_packet->sys_cmd[i].cmd.id == CMD_ACK_ID; i++) { if(r_packet->sys_cmd[i].cmd.id == CMD_ACK_ID) { /* Check if ACK and NAK commands are the first system commands */ if(ret!=-2 && ret==i-1) { ret = i; } else { ret = -2; } /* If this is not the last ACK command in the sequence of * ACK/NAK commands, then remove all packets from history of * sent packet, that are in following sub-sequence of ACK * commands */ if(r_packet->sys_cmd[i+1].cmd.id == CMD_NAK_ID || r_packet->sys_cmd[i+1].cmd.id == CMD_ACK_ID) { /* Remove all acknowledged payload packets from the history * of sent payload packets */ for(ack_id = r_packet->sys_cmd[i].ack_cmd.pay_id; ack_id < r_packet->sys_cmd[i+1].nak_cmd.pay_id; ack_id++) { v_packet_history_rem_packet(C, ack_id); } } else { /* Remove this acknowledged payload packets from the history * of sent payload packets */ v_packet_history_rem_packet(C, r_packet->sys_cmd[i].ack_cmd.pay_id); /* This is the last ACK command in the sequence of ACK/NAK * commands. Update ANK ID. */ vconn->ank_id = r_packet->sys_cmd[i].ack_cmd.pay_id; } } else if(r_packet->sys_cmd[i].cmd.id == CMD_NAK_ID) { /* Check if ACK and NAK commands are the first system commands */ if(ret!=-2 && ret==i-1) { ret = i; } else { ret = -2; } /* Go through the sub-sequence of NAk commands and try to re-send * not-obsolete data from these packets */ for(nak_id = r_packet->sys_cmd[i].nak_cmd.pay_id; nak_id < r_packet->sys_cmd[i+1].ack_cmd.pay_id; nak_id++) { /* Add not obsolete data of lost packet to the outgoing queue */ /* Update ANK ID */ sent_packet = v_packet_history_find_packet(&vconn->packet_history, nak_id); if(sent_packet != NULL) { v_print_log(VRS_PRINT_DEBUG_MSG, "Try to re-send packet: %d\n", nak_id); sent_cmd = sent_packet->cmds.last; /* Go through all commands in command list and add not * obsolete commands to the outgoing queue */ while(sent_cmd != NULL) { sent_cmd_prev = sent_cmd->prev; if(sent_cmd->vbucket != NULL && sent_cmd->vbucket->data != NULL) { /* Try to add command back to the outgoing command queue */ if(v_out_queue_push_head(vsession->out_queue, sent_cmd->prio, (struct Generic_Cmd*)sent_cmd->vbucket->data) == 1) { /* Remove bucket from the history of sent commands too */ v_hash_array_remove_item(&vconn->packet_history.cmd_hist[sent_cmd->id]->cmds, sent_cmd->vbucket->data); /* When command was added back to the queue, * then delete only sent command */ v_list_free_item(&sent_packet->cmds, sent_cmd); } } sent_cmd = sent_cmd_prev; } /* When all not obsolete commands are added to outgoing * queue, then this packet could be removed from packet * history*/ v_packet_history_rem_packet(C, nak_id); } } } } return ret; }
/** * \brief This function tries to handle Tag_Destroy command */ int vs_handle_tag_destroy_ack(struct VS_CTX *vs_ctx, struct VSession *vsession, struct Generic_Cmd *cmd) { struct VSNode *node; struct VSTagGroup *tg; struct VSTag *tag; struct VSEntityFollower *tag_follower; struct Tag_Destroy_Ack_Cmd *tag_destroy_ack = (struct Tag_Destroy_Ack_Cmd*)cmd; uint32 node_id = tag_destroy_ack->node_id; uint16 taggroup_id = tag_destroy_ack->taggroup_id; uint16 tag_id = tag_destroy_ack->tag_id; int ret = 0; /* Try to find node */ if((node = vs_node_find(vs_ctx, node_id)) == NULL) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s() node (id: %d) not found\n", __FUNCTION__, node_id); return 0; } pthread_mutex_lock(&node->mutex); /* Try to find TagGroup */ if( (tg = vs_taggroup_find(node, taggroup_id)) == NULL) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s() tag_group (id: %d) in node (id: %d) not found\n", __FUNCTION__, taggroup_id, node_id); goto end; } /* Try to find Tag */ if ( (tag = vs_tag_find(tg, tag_id)) == NULL) { v_print_log(VRS_PRINT_DEBUG_MSG, "%s() tag (id: %d) in tag_group (id: %d), node (id: %d) not found\n", __FUNCTION__, tag_id, taggroup_id, node_id); goto end; } /* Try to find tag follower that generated this fake command */ tag_follower = tag->tag_folls.first; while(tag_follower != NULL) { if(tag_follower->node_sub->session->session_id == vsession->session_id) { tag_follower->state = ENTITY_DELETED; v_list_free_item(&tag->tag_folls, tag_follower); break; } tag_follower = tag_follower->next; } /* When tag doesn't have any follower, then it is possible to destroy * this tag and remove it from list of tags from the tag group */ if(tag->tag_folls.first == NULL) { tag->state = ENTITY_DELETED; vs_tag_destroy(tg, tag); } ret = 1; end: pthread_mutex_unlock(&node->mutex); return ret; }