/** * \brief This function destroy structure storing information about tag. This * function should be called only in situation, when all clients received * TagDestroy command. */ int vs_tag_destroy(struct VSTagGroup *tg, struct VSTag *tag) { if(tag->tag_folls.first == NULL) { /* Free value */ if(tag->value != NULL) { free(tag->value); tag->value = NULL; } /* Remove tag from tag group */ v_hash_array_remove_item(&tg->tags, tag); free(tag); vs_taggroup_inc_version(tg); return 1; } else { /* This should never happen */ v_print_log(VRS_PRINT_WARNING, "%s(): tag (id: %d) with followers can't be destroyed\n", __FUNCTION__, tag->id); return 0; } return 0; }
/** * \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 tries to unset value in the layer and all child layers * * This function is called for parent layer and it is called recursively to * unset values in all child layers * * \param[in] *node The pointer at node * \param[in] *layer The pointer at layer * \param[in] item_id The ID of item which should be unset (deleted) * \param[in] send_command If this argument is equal to 1, then unset_value * command is sent to all subscribers (should be equal to1 only for parent layer) * * \return This function returns 1, when it was able to unset value. Otherwise * it returns 0. */ static int vs_layer_unset_value(struct VSNode *node, struct VSLayer *layer, uint32 item_id, uint8 send_command) { struct VSLayer *child_layer; struct VSLayerValue *item = NULL, _item; struct VBucket *vbucket; struct VSEntitySubscriber *layer_subscriber; /* Try to find item value first */ _item.id = item_id; vbucket = v_hash_array_find_item(&layer->values, &_item); if(vbucket != NULL) { item = (struct VSLayerValue*)vbucket->data; /* Send unset command only for parent layer */ if(send_command == 1) { /* Send item value unset to all layer subscribers */ layer_subscriber = layer->layer_subs.first; while(layer_subscriber != NULL) { vs_layer_send_unset_value(layer_subscriber, node, layer, item); layer_subscriber = layer_subscriber->next; } } /* Free value */ free(item->value); /* Remove value from hashed array */ v_hash_array_remove_item(&layer->values, item); /* Free item */ free(item); } else { return 0; } /* Try to unset values in all child values, but don't send unset_value command * about this unsetting, because client will receive layer_unset of parent * layer*/ child_layer = layer->child_layers.first; while(child_layer != NULL) { vs_layer_unset_value(node, child_layer, item_id, 0); child_layer = child_layer->next; } return 1; }
/** * \brief This function removes all tag groups and tags from the VSNode. This * function doesn't check, if any client is subscribed to tag groups or not. * This function should be called only in situation, when node is destroyed. */ int vs_node_taggroups_destroy(struct VSNode *node) { struct VBucket *tg_bucket, *tg_bucket_next, *bucket; struct VSTagGroup *tg; struct VSTag *tag; tg_bucket = node->tag_groups.lb.first; while(tg_bucket != NULL) { tg_bucket_next = tg_bucket->next; tg = (struct VSTagGroup*)tg_bucket->data; /* Free all data allocated in tags at the first time */ bucket = tg->tags.lb.first; while(bucket != NULL) { tag = (struct VSTag*)bucket->data; if(tag->value) { free(tag->value); tag->value = NULL; } free(tag); bucket = bucket->next; } /* Destroy all tags in this taggroup */ v_hash_array_destroy(&tg->tags); /* Free list of followers and subscribers */ v_list_free(&tg->tg_folls); v_list_free(&tg->tg_subs); /* Destroy this tag group itself */ v_hash_array_remove_item(&node->tag_groups, tg); free(tg); tg_bucket = tg_bucket_next; } return 1; }
/** * \brief This function destroys layer stored at verse server */ void vs_layer_destroy(struct VSNode *node, struct VSLayer *layer) { struct VSLayer *child_layer; struct VSLayerValue *item; struct VBucket *vbucket; /* Free values in all items */ vbucket = (struct VBucket*)layer->values.lb.first; while(vbucket != NULL) { item = (struct VSLayerValue*)vbucket->data; free(item->value); vbucket = vbucket->next; } /* Destroy hashed array with items */ v_hash_array_destroy(&layer->values); /* Set references to parent layer in all child layers to NULL */ child_layer = layer->child_layers.first; while(child_layer != NULL) { child_layer->parent = NULL; child_layer = child_layer->next; } layer->child_layers.first = NULL; layer->child_layers.last = NULL; /* If this layer has parent layer, then remove this layer from * parent linked list of child layers */ if(layer->parent != NULL) { v_list_rem_item(&layer->parent->child_layers, layer); } /* Free list of followers and subscribers */ v_list_free(&layer->layer_folls); v_list_free(&layer->layer_subs); v_print_log(VRS_PRINT_DEBUG_MSG, "Layer: %d destroyed\n", layer->id); /* Destroy this layer itself */ v_hash_array_remove_item(&node->layers, layer); free(layer); }
/** * \brief This function destroy tag group and all tags included in this tag * group. This function should be called only in situation, when all clients * received command 'TagGroup Destroy'. */ int vs_taggroup_destroy(struct VSNode *node, struct VSTagGroup *tg) { struct VBucket *bucket; struct VSTag *tag; /* All clients had to received TagGroup_Destroy command */ assert(tg->tg_folls.first == NULL); /* Free all data allocated in tags at the first time */ bucket = tg->tags.lb.first; while(bucket != NULL) { tag = (struct VSTag*)bucket->data; if(tag->value != NULL) { free(tag->value); tag->value = NULL; } free(tag); bucket = bucket->next; } /* Destroy all tags in this taggroup */ v_hash_array_destroy(&tg->tags); /* Free list of followers and subscribers */ v_list_free(&tg->tg_folls); v_list_free(&tg->tg_subs); v_print_log(VRS_PRINT_DEBUG_MSG, "TagGroup: %d destroyed\n", tg->id); /* Destroy this tag group itself */ v_hash_array_remove_item(&node->tag_groups, tg); free(tg); vs_node_inc_version(node); return 1; }
/* * \brief This function pop command from queue with specific priority. * * Returned command is allocated on the heap and have to be free from calling * function. When *count is not NULL, then this function will write number of * commands with same ID that could be compressed on this address. When *len * is not NULL, then this function will write length of compressed commands * at this address. When *len value is not 0, then this value represents maximal * size of commands that could be removed from this priority queue. * * \param[in] *out_queue The pointer at list of priority queues * \param[in] prio The value of priority * \param[out] *count The pointer at count of commands with same id that * could be compressed. * \param[out] *share The size of address that could be shared * \param[out] *len The length of compressed commands. * * \return This function returns pointer at command. This command is allocated * at the heap. */ struct Generic_Cmd *v_out_queue_pop(struct VOutQueue *out_queue, uint8 prio, uint16 *count, int8 *share, uint16 *len) { struct VPrioOutQueue *prio_queue = out_queue->queues[prio]; struct VOutQueueCommand *queue_cmd; struct Generic_Cmd *cmd=NULL; int i, can_pop_cmd = 1; /* Lock mutex */ pthread_mutex_lock(&out_queue->lock); /*printf("\tcount: %u, share: %u, len: %u\n", *count, *share, *len);*/ queue_cmd = prio_queue->cmds.first; if(queue_cmd != NULL ) { cmd = (struct Generic_Cmd *)queue_cmd->vbucket->data; assert(cmd != NULL); /* Return value of count and length of compressed commands */ if(queue_cmd->counter != NULL) { if(count != NULL) { *count = *queue_cmd->counter; } if(share != NULL) { *share = *queue_cmd->share; } if(len != NULL) { /* When *len value is not 0, then this value represents maximal * size of buffer that could be filled with this priority queue. This * function should return value, that is smaller or equal than this * maximum. */ if(*len != 0) { if(*len < *queue_cmd->len) { if (count != NULL) { *count = v_cmd_count(cmd, *len, *queue_cmd->share); /* FIXME: compute length and count correctly, when command is added to the queue */ *count = ((*count) > (*queue_cmd->counter)) ? *queue_cmd->counter : *count; /* Is enough space in buffer to unpack this command? */ if(*count == 0) { can_pop_cmd = 0; } *len = v_cmds_len(cmd, *count, *queue_cmd->share, 0); } else { can_pop_cmd = 0; } } else { *len = *queue_cmd->len; } } else { *len = *queue_cmd->len; } } } else { if(count != NULL) { *count = 0; } if(share != NULL) { *share = 0; } if(len != NULL) { if(v_cmd_size(cmd) > *len) { can_pop_cmd = 0; } *len = 0; } } /* Is it possible to pop command from the queue */ if(can_pop_cmd == 1) { /* Remove command from hashed linked list */ v_hash_array_remove_item(&out_queue->cmds[cmd->id]->cmds, (void*)cmd); /* Remove command from priority queue */ v_list_rem_item(&prio_queue->cmds, queue_cmd); /* Update total count and size of commands */ out_queue->count--; out_queue->size -= out_queue->cmds[cmd->id]->item_size; /* Update count and size of commands in subqueue with priority prio */ out_queue->queues[prio]->count--; out_queue->queues[prio]->size -= out_queue->cmds[cmd->id]->item_size; /* If needed, then update counter of commands with the same id in the queue */ if(queue_cmd->counter != NULL) { *queue_cmd->counter -= 1; /* Free values, when it's last command in the queue */ if(*queue_cmd->counter == 0) { free(queue_cmd->counter); queue_cmd->counter = NULL; free(queue_cmd->share); queue_cmd->share = NULL; free(queue_cmd->len); queue_cmd->len = NULL; } } /* When this priority queue is empty now, then update summary of * real priorities and it is possibly necessary to change maximal * or minimal priority queue*/ if(out_queue->queues[prio]->count == 0) { if(prio>=VRS_DEFAULT_PRIORITY) { out_queue->r_prio_sum_high -= out_queue->queues[prio]->r_prio; } else { out_queue->r_prio_sum_low -= out_queue->queues[prio]->r_prio; } if(out_queue->max_prio == prio && out_queue->min_prio != prio) { for(i=prio; i>=out_queue->min_prio; i--) { if(out_queue->queues[i]->count != 0) { out_queue->max_prio = i; break; } } } else if(out_queue->min_prio == prio && out_queue->max_prio != prio) { for(i=prio; i<=out_queue->max_prio; i++) { if(out_queue->queues[i]->count != 0) { out_queue->min_prio = i; break; } } } else if(out_queue->min_prio == prio && out_queue->max_prio == prio) { out_queue->min_prio = out_queue->max_prio = VRS_DEFAULT_PRIORITY; } } /* Free queue command */ free(queue_cmd); } else { cmd = NULL; } } pthread_mutex_unlock(&out_queue->lock); return cmd; }
/** * \brief This function adds command to the head or tail of the queue */ static int _v_out_queue_push(struct VOutQueue *out_queue, uint8 flag, uint8 prio, struct Generic_Cmd *cmd) { struct VOutQueueCommand *queue_cmd; struct VBucket *vbucket; int ret = 1; assert(cmd!=NULL); /* Try to find command with the same address, when duplicities are not * allowed in command queue */ if(out_queue->cmds[cmd->id]->flag & REMOVE_HASH_DUPS) { vbucket = v_hash_array_find_item(&out_queue->cmds[cmd->id]->cmds, (void*)cmd); if(vbucket != NULL) { /* Bucket has to include not NULL pointer */ assert(vbucket->ptr!=NULL); queue_cmd = (struct VOutQueueCommand*)vbucket->ptr; /* Remove old command from the queue if the priority is different * and add command to the end of new priority queue */ if(queue_cmd->prio != prio) { /* Remove old obsolete data */ v_hash_array_remove_item(&out_queue->cmds[cmd->id]->cmds, vbucket->data); /* Remove old command */ v_list_rem_item(&out_queue->queues[queue_cmd->prio]->cmds, queue_cmd); /* Update size and count in old priority queue */ out_queue->queues[queue_cmd->prio]->count--; out_queue->queues[queue_cmd->prio]->size -= out_queue->cmds[cmd->id]->item_size; /* If needed, then update counter of commands with the same id in the queue */ if(queue_cmd->counter != NULL) { *queue_cmd->counter -= 1; if(*queue_cmd->counter == 0) { free(queue_cmd->counter); queue_cmd->counter = NULL; free(queue_cmd->share); queue_cmd->share = NULL; free(queue_cmd->len); queue_cmd->len = NULL; } } /* When this priority queue is empty now, then update summary of real priorities */ if(out_queue->queues[queue_cmd->prio]->count == 0) { if(prio>=VRS_DEFAULT_PRIORITY) out_queue->r_prio_sum_high -= out_queue->queues[queue_cmd->prio]->r_prio; else out_queue->r_prio_sum_low -= out_queue->queues[queue_cmd->prio]->r_prio; } /* Update new priority command */ queue_cmd->id = cmd->id; queue_cmd->prio = prio; if(out_queue->cmds[cmd->id]->flag & NOT_SHARE_ADDR) { _v_out_queue_command_add(out_queue->queues[prio], flag, 0, queue_cmd, cmd); } else { _v_out_queue_command_add(out_queue->queues[prio], flag, 1, queue_cmd, cmd); } /* Add data of the command to the hashed linked list */ queue_cmd->vbucket = v_hash_array_add_item(&out_queue->cmds[cmd->id]->cmds, cmd, out_queue->cmds[cmd->id]->item_size); queue_cmd->vbucket->ptr = (void*)queue_cmd; assert(queue_cmd->vbucket->data != NULL); /* When this priority queue was empty, then update summary of real priorities */ if(out_queue->queues[prio]->count == 0) { if(prio>=VRS_DEFAULT_PRIORITY) out_queue->r_prio_sum_high += out_queue->queues[prio]->r_prio; else out_queue->r_prio_sum_low += out_queue->queues[prio]->r_prio; } /* Update count and size in new priority queue */ out_queue->queues[prio]->count++; out_queue->queues[prio]->size += out_queue->cmds[cmd->id]->item_size; /* If necessary update maximal or minimal priority */ if(prio > out_queue->max_prio) { out_queue->max_prio = prio; } else if(prio < out_queue->min_prio) { out_queue->min_prio = prio; } } else { /* Debug print */ #if 0 v_print_log(VRS_PRINT_DEBUG_MSG, "Replacing obsolete command with new\n"); v_cmd_print(VRS_PRINT_DEBUG_MSG, (struct Generic_Cmd*)vbucket->data); v_cmd_print(VRS_PRINT_DEBUG_MSG, cmd); #endif /* Destroy original command */ v_cmd_destroy((struct Generic_Cmd**)&vbucket->data); /* Replace data of current queue command with new data */ vbucket->data = (void*)cmd; } } else { /* Add new command in queue */ queue_cmd = _v_out_queue_command_create(out_queue, flag, prio, cmd); if(queue_cmd == NULL) { ret = 0; } } } else { /* Add new command in queue */ queue_cmd = _v_out_queue_command_create(out_queue, flag, prio, cmd); if(queue_cmd == NULL) { ret = 0; } } return ret; }
/** * \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 removes packet with id from history of sent packets. * It removes all its commands from the command history too. * * \param[in] *C The verse context. * \param[in] id The ID of packet, the will be removed from the history. * * \return This function returns 1, then packet with id was found in the history * and it returns 0 otherwise. */ int v_packet_history_rem_packet(struct vContext *C, uint32 id) { struct VS_CTX *vs_ctx = CTX_server_ctx(C); struct VDgramConn *dgram_conn = CTX_current_dgram_conn(C); struct VPacket_History *history = &dgram_conn->packet_history; struct VPacket *r_packet = CTX_r_packet(C); struct VSent_Packet *sent_packet; int ret = 0, is_fake_cmd_received = 0; sent_packet = v_packet_history_find_packet(history, id); if(sent_packet != NULL) { struct VSent_Command *sent_cmd; v_print_log(VRS_PRINT_DEBUG_MSG, "Removing packet: %d from history\n", sent_packet->id); /* Go through the whole list of sent commands and free them from the * hashed linked list */ sent_cmd = sent_packet->cmds.first; while(sent_cmd != NULL) { /* Remove own command from hashed linked list if it wasn't already * removed, when command was obsoleted by some newer packet */ if(sent_cmd->vbucket!=NULL) { struct Generic_Cmd *cmd = (struct Generic_Cmd*)sent_cmd->vbucket->data; /* Bucket has to include some data */ assert(sent_cmd->vbucket->data!=NULL); /* Decrease total size of commands that were sent and wasn't acknowladged yet*/ dgram_conn->sent_size -= v_cmd_size(cmd); /* Put fake command for create/destroy commands at verse server */ if(vs_ctx != NULL) { struct VSession *vsession = CTX_current_session(C); struct Generic_Cmd *fake_cmd = NULL; switch(cmd->id) { case CMD_NODE_CREATE: fake_cmd = v_fake_node_create_ack_create(UINT32(cmd->data[UINT16_SIZE + UINT32_SIZE])); break; case CMD_NODE_DESTROY: fake_cmd = v_fake_node_destroy_ack_create(UINT32(cmd->data[0])); break; case CMD_TAGGROUP_CREATE: fake_cmd = v_fake_taggroup_create_ack_create(UINT32(cmd->data[0]), UINT16(cmd->data[UINT32_SIZE])); break; case CMD_TAGGROUP_DESTROY: fake_cmd = v_fake_taggroup_destroy_ack_create(UINT32(cmd->data[0]), UINT16(cmd->data[UINT32_SIZE])); break; case CMD_TAG_CREATE: fake_cmd = v_tag_create_ack_create(UINT32(cmd->data[0]), UINT16(cmd->data[UINT32_SIZE]), UINT16(cmd->data[UINT32_SIZE + UINT16_SIZE])); break; case CMD_TAG_DESTROY: fake_cmd = v_tag_destroy_ack_create(UINT32(cmd->data[0]), UINT16(cmd->data[UINT32_SIZE]), UINT16(cmd->data[UINT32_SIZE + UINT16_SIZE])); break; case CMD_LAYER_CREATE: fake_cmd = v_fake_layer_create_ack_create(UINT32(cmd->data[0]), UINT16(cmd->data[UINT32_SIZE + UINT16_SIZE])); break; case CMD_LAYER_DESTROY: fake_cmd = v_fake_layer_destroy_ack_create(UINT32(cmd->data[0]), UINT16(cmd->data[UINT32_SIZE])); break; default: break; } if(fake_cmd != NULL) { is_fake_cmd_received = 1; /* Push command to the queue of incomming commands */ v_in_queue_push(vsession->in_queue, fake_cmd); /* Print content of fake command */ v_fake_cmd_print(VRS_PRINT_DEBUG_MSG, fake_cmd); } } /* Remove command from the history of sent commands */ ret = v_hash_array_remove_item(&history->cmd_hist[sent_cmd->id]->cmds, sent_cmd->vbucket->data); if(ret == 1) { /* Destroy command */ v_cmd_destroy(&cmd); } else { v_print_log(VRS_PRINT_ERROR, "Unable to remove command id: %d\n", sent_cmd->id); ret = 0; } } sent_cmd = sent_cmd->next; } /* Free linked list of sent commands */ v_list_free(&sent_packet->cmds); /* Remove packet itself from the linked list of sent packet */ v_list_rem_item(&history->packets, sent_packet); free(sent_packet); ret = 1; } else { /* When acknowledged payload packet is not found in history, then it * is OK, because it is probably keep alive packet without any node * commands */ v_print_log(VRS_PRINT_DEBUG_MSG, "Packet with id: %d, not found in history\n", id); } /* When pure ack packet caused adding some fake commands to the queue, then * poke server data thread */ if( (vs_ctx != NULL) && (is_fake_cmd_received == 1) && (r_packet->data == NULL)) { sem_post(vs_ctx->data.sem); } return ret; }
/** * \brief This function add command to the history of sent command * * When this new added command updates some recent command with * the same address, then this command is set as obsolete. Pointer at this * command is set to NULL in previous packet. * * \param[in] *history The structure containing history of sent packets * and commands * \param[in] *sent_packet The current packet * \param[in] *cmd The data of command * * \return This function returns 1, when command was added to history. * When it wasn't able to add command to history, then zero is returned. */ int v_packet_history_add_cmd(struct VPacket_History *history, struct VSent_Packet *sent_packet, struct Generic_Cmd *cmd, uint8 prio) { struct VSent_Command *sent_cmd; struct VBucket *vbucket; void *_cmd = (void*)cmd; uint8 cmd_id = cmd->id; uint16 cmd_size = v_cmd_struct_size(cmd); int ret = 0; /* Are duplications allowed for this type of commands? */ if(history->cmd_hist[cmd_id]->flag & REMOVE_HASH_DUPS) { /* Try to find command with the same address */ vbucket = v_hash_array_find_item(&history->cmd_hist[cmd_id]->cmds, _cmd); if(vbucket != NULL) { struct Generic_Cmd *obsolete_cmd = (struct Generic_Cmd*)vbucket->data; /* Bucket has to include not NULL pointer */ assert(vbucket->ptr!=NULL); assert(vbucket->data!=NULL); /* Debug print */ #if 0 v_print_log(VRS_PRINT_INFO, "Replacing obsolete command\n"); v_cmd_print(VRS_PRINT_INFO, obsolete_cmd); v_cmd_print(VRS_PRINT_INFO, cmd); #endif /* When old data are obsolete, then set pointer at command in old * command to the NULL (obsolete command would not be re-send) */ ((struct VSent_Command*)(vbucket->ptr))->vbucket = NULL; /* Remove data with the same key as cmd_data has from hashed linked * list */ ret = v_hash_array_remove_item(&history->cmd_hist[cmd_id]->cmds, vbucket->data); if(ret == 1) { /* Destroy original command */ v_cmd_destroy(&obsolete_cmd); } else { v_print_log(VRS_PRINT_DEBUG_MSG, "Could not remove obsolete command (id: %d) from history\n", cmd_id); ret = 0; } } } /* Add own command data to the hashed linked list */ vbucket = v_hash_array_add_item(&history->cmd_hist[cmd_id]->cmds, _cmd, cmd_size); if(vbucket != NULL) { /* Create new command */ sent_cmd = (struct VSent_Command*)malloc(sizeof(struct VSent_Command)); /* Check if it was possible to allocate enough memory for sent command */ if(sent_cmd != NULL) { sent_cmd->id = cmd_id; /* Add command to the linked list of sent packet */ v_list_add_tail(&sent_packet->cmds, sent_cmd); /* Set up pointer at command data */ sent_cmd->vbucket = vbucket; /* Set up pointer at owner of this command item to be able to obsolete * this command in future */ vbucket->ptr = (void*)sent_cmd; /* Store information about command priority. Lost commands should * be re-send with same priority*/ sent_cmd->prio = prio; ret = 1; } else { /* When memory wasn't allocated, then free bucket from hashed * linked list */ v_print_log(VRS_PRINT_ERROR, "Unable allocate enough memory for sent command\n"); ret = v_hash_array_remove_item(&history->cmd_hist[cmd_id]->cmds, _cmd); if(ret == 1) { v_cmd_destroy(_cmd); } ret = 0; } } else { v_print_log(VRS_PRINT_ERROR, "Unable to add command (id: %d) to packet history\n", cmd_id); ret = 0; } return ret; }