/** * \brief This function add command to the head of the queue */ int v_out_queue_push_head(struct VOutQueue *out_queue, uint8 prio, struct Generic_Cmd *cmd) { struct VBucket *vbucket; int ret = 0; /* Lock mutex */ pthread_mutex_lock(&out_queue->lock); /* 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); /* Add command from history of sent command to the outgoing queue * only in situation, when there isn't already newer command in the * queue */ if(vbucket==NULL) { v_print_log_simple(VRS_PRINT_DEBUG_MSG, "\tRe-sending command: %d\n", cmd->id); v_cmd_print(VRS_PRINT_DEBUG_MSG, (struct Generic_Cmd*)cmd); ret = _v_out_queue_push(out_queue, OUT_QUEUE_ADD_HEAD, prio, cmd); } } else { v_print_log_simple(VRS_PRINT_DEBUG_MSG, "\tRe-sending command: %d\n", cmd->id); v_cmd_print(VRS_PRINT_DEBUG_MSG, (struct Generic_Cmd*)cmd); ret = _v_out_queue_push(out_queue, OUT_QUEUE_ADD_HEAD, prio, cmd); } pthread_mutex_unlock(&out_queue->lock); return ret; }
/** * \brief This function prints history of sent packets * \param[in] *history The history of sent packets. */ void v_print_packet_history(struct VPacket_History *history) { struct VSent_Packet *packet; struct VBucket *vbucket; int cmd_id; v_print_log(VRS_PRINT_DEBUG_MSG, "Packet history:\n\t"); packet = history->packets.first; while(packet!=NULL) { v_print_log_simple(VRS_PRINT_DEBUG_MSG, "%d, ", packet->id); packet = packet->next; } v_print_log_simple(VRS_PRINT_DEBUG_MSG, "\n"); v_print_log(VRS_PRINT_DEBUG_MSG, "Command history:\n"); for(cmd_id=0; cmd_id<=MAX_CMD_ID; cmd_id++) { if(history->cmd_hist[cmd_id] != NULL) { vbucket = history->cmd_hist[cmd_id]->cmds.lb.first; while(vbucket != NULL) { v_cmd_print(VRS_PRINT_DEBUG_MSG, (struct Generic_Cmd*)vbucket->data); vbucket = vbucket->next; } } } }
/** * \brief This function try to pack message that is going to be * sent in STREAM OPEN state * * \param[in] *C The pointer at context * * \return This function return 1, when there is something to send, * it returns -1, when there is nothing to send and it returns 0, when * there is some error */ int v_STREAM_pack_message(struct vContext *C) { struct VS_CTX *vs_ctx = CTX_server_ctx(C); struct VSession *vsession = CTX_current_session(C); struct VStreamConn *conn = CTX_current_stream_conn(C); struct IO_CTX *io_ctx = CTX_io_ctx(C); struct VMessage *s_message = CTX_s_message(C); struct Generic_Cmd *cmd, *fake_cmd; int ret = -1, queue_size = 0, buffer_pos = 0, prio_cmd_count, cmd_rank=0; int8 cmd_share; int16 prio, max_prio, min_prio; uint16 cmd_count, cmd_len, prio_win, swin, sent_size, tot_cmd_size; real32 prio_sum_high, prio_sum_low, r_prio; int is_fake_cmd_received = 0; /* Is here something to send? */ if((v_out_queue_get_count(vsession->out_queue) > 0) || (vsession->tmp_flags & SYS_CMD_NEGOTIATE_FPS)) { /* Get current size of data in TCP outgoing buffer */ #ifdef __linux__ if( ioctl(io_ctx->sockfd, SIOCOUTQ, &queue_size) == -1 ) { v_print_log(VRS_PRINT_ERROR, "ioctl(%d, SIOCOUTQ, ...): %s\n", io_ctx->sockfd, strerror(errno)); return 0; } #endif /* Compute, how many data could be added to the TCP stack? */ swin = conn->socket_buffer_size - queue_size; buffer_pos = VERSE_MESSAGE_HEADER_SIZE; s_message->sys_cmd[0].cmd.id = CMD_RESERVED_ID; /* When negotiated and used FPS is different, then pack negotiate command * for FPS */ if(vsession->fps_host != vsession->fps_peer) { cmd_rank += v_add_negotiate_cmd(s_message->sys_cmd, cmd_rank, CMD_CHANGE_L_ID, FTR_FPS, &vsession->fps_host, NULL); } else { if(vsession->tmp_flags & SYS_CMD_NEGOTIATE_FPS) { cmd_rank += v_add_negotiate_cmd(s_message->sys_cmd, cmd_rank, CMD_CONFIRM_L_ID, FTR_FPS, &vsession->fps_peer, NULL); /* Send confirmation only once for received system command */ vsession->tmp_flags &= ~SYS_CMD_NEGOTIATE_FPS; } } buffer_pos += v_pack_stream_system_commands(s_message, &io_ctx->buf[buffer_pos]); max_prio = v_out_queue_get_max_prio(vsession->out_queue); min_prio = v_out_queue_get_min_prio(vsession->out_queue); prio_sum_high = v_out_queue_get_prio_sum_high(vsession->out_queue); prio_sum_low = v_out_queue_get_prio_sum_low(vsession->out_queue); v_print_log(VRS_PRINT_DEBUG_MSG, "Packing prio queues, cmd count: %d\n", v_out_queue_get_count(vsession->out_queue)); /* Go through all priorities and pick commands from priority queues */ for(prio = max_prio; prio >= min_prio; prio--) { prio_cmd_count = v_out_queue_get_count_prio(vsession->out_queue, prio); if(prio_cmd_count > 0) { r_prio = v_out_queue_get_prio(vsession->out_queue, prio); /* Compute size of buffer that could be occupied by * commands from this queue */ if(prio >= VRS_DEFAULT_PRIORITY) { prio_win = ((swin - buffer_pos)*r_prio)/prio_sum_high; } else { prio_win = ((swin - buffer_pos)*r_prio)/prio_sum_low; } /* Debug print */ v_print_log(VRS_PRINT_DEBUG_MSG, "Queue: %d, count: %d, r_prio: %6.3f, prio_win: %d\n", prio, prio_cmd_count, r_prio, prio_win); /* Get total size of commands that were stored in queue (sent_size) */ sent_size = 0; tot_cmd_size = 0; while(prio_cmd_count > 0) { cmd_share = 0; cmd_count = 0; cmd_len = prio_win - sent_size; /* Pack commands from queues with high priority to the buffer */ cmd = v_out_queue_pop(vsession->out_queue, prio, &cmd_count, &cmd_share, &cmd_len); if(cmd != NULL) { /* Is this command fake command? */ if(cmd->id < MIN_CMD_ID) { if(cmd->id == FAKE_CMD_CONNECT_TERMINATE) { /* Close connection */ struct VS_CTX *vs_ctx = CTX_server_ctx(C); if(vs_ctx != NULL) { vsession->stream_conn->host_state = TCP_SERVER_STATE_CLOSING; } else { vsession->stream_conn->host_state = TCP_CLIENT_STATE_CLOSING; } } else if(cmd->id == FAKE_CMD_FPS) { struct Fps_Cmd *fps_cmd = (struct Fps_Cmd*)cmd; /* Change value of FPS. It will be sent in negotiate command * until it is confirmed be the peer (server) */ vsession->fps_host = fps_cmd->fps; } } else { buffer_pos += tot_cmd_size = v_cmd_pack(&io_ctx->buf[buffer_pos], cmd, v_cmd_size(cmd), 0); if(is_log_level(VRS_PRINT_DEBUG_MSG)) { printf("%c[%d;%dm", 27, 1, 32); v_cmd_print(VRS_PRINT_DEBUG_MSG, cmd); printf("%c[%dm", 27, 0); } sent_size += tot_cmd_size; } fake_cmd = v_cmd_fake_ack(cmd); if(fake_cmd != NULL) { is_fake_cmd_received = 1; /* Push command to the queue of incoming 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); } /* It is not necessary to put cmd to history of sent commands, * when TCP is used. */ v_cmd_destroy(&cmd); prio_cmd_count--; } else { break; } } } } s_message->header.len = io_ctx->buf_size = buffer_pos; s_message->header.version = VRS_VERSION; /* Pack header to the beginning of the buffer */ v_pack_message_header(s_message, io_ctx->buf); /* Debug print of command to be send */ if(is_log_level(VRS_PRINT_DEBUG_MSG)) { v_print_send_message(C); } /* 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)) { sem_post(vs_ctx->data.sem); } ret = 1; } return ret; }
/** * \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 packs and compress command to the packet from one * priority queue. * * \param[in] *C The verse context * \param[in] *sent_packet The pointer at structure with send packet * \param[in] buffer_pos The curent size of buffer of sent packet * \param[in] prio The priority of sub queue * \param[in] prio_win The window size of current prio queue * \param[out] tot_cmd_size The total size of commands that were poped from prio queue */ static int pack_prio_queue(struct vContext *C, struct VSent_Packet *sent_packet, int buffer_pos, uint8 prio, uint16 prio_win, uint16 *tot_cmd_size) { struct VSession *vsession = CTX_current_session(C); struct VDgramConn *vconn = CTX_current_dgram_conn(C); struct IO_CTX *io_ctx = CTX_io_ctx(C); struct Generic_Cmd *cmd; int ret, last_cmd_count = 0; uint16 cmd_count, cmd_len, cmd_size, sum_len=0; int8 cmd_share; uint8 last_cmd_id = CMD_RESERVED_ID; while( (v_out_queue_get_count_prio(vsession->out_queue, prio) > 0) && (sum_len < prio_win) && (buffer_pos < vconn->io_ctx.mtu)) { cmd_count = 0; cmd_share = 0; /* Compute how many commands could be compressed to the packet * and compute right length of compressed commands. */ cmd_len = ((prio_win - sum_len)<(vconn->io_ctx.mtu - buffer_pos)) ? (prio_win - sum_len) : (vconn->io_ctx.mtu - buffer_pos); /* Remove command from queue */ cmd = v_out_queue_pop(vsession->out_queue, prio, &cmd_count, &cmd_share, &cmd_len); /* When it is not possible to pop more commands from queue, then break * while loop */ if(cmd == NULL) { break; } /* Is this command fake command? */ if(cmd->id < MIN_CMD_ID) { if(cmd->id == FAKE_CMD_CONNECT_TERMINATE) { struct VS_CTX *vs_ctx = CTX_server_ctx(C); if(vs_ctx != NULL) { vconn->host_state = UDP_SERVER_STATE_CLOSEREQ; } else { vconn->host_state = UDP_CLIENT_STATE_CLOSING; } } else if(cmd->id == FAKE_CMD_FPS) { struct Fps_Cmd *fps_cmd = (struct Fps_Cmd*)cmd; /* Change value of FPS. It will be sent in negotiate command * until it is confirmed be the peer (server) */ vsession->fps_host = fps_cmd->fps; } v_cmd_destroy(&cmd); } else { /* What was size of command in queue */ cmd_size = v_cmd_size(cmd); if(!(buffer_pos < (vconn->io_ctx.mtu - cmd_size))) { /* When there is not enough space for other command, * then push command back to the beginning of queue. */ v_out_queue_push_head(vsession->out_queue, prio, cmd); break; } else { /* Update total size of commands that were poped from queue */ *tot_cmd_size += cmd_size; /* When compression is not allowed, then add this command as is */ if( vconn->host_cmd_cmpr == CMPR_NONE) { cmd_count = 0; cmd_len = cmd_size; /* Debug print */ v_print_log(VRS_PRINT_DEBUG_MSG, "Cmd: %d, count: %d, length: %d\n", cmd->id, cmd_count, cmd_len); /* Add command to the buffer */ buffer_pos += v_cmd_pack(&io_ctx->buf[buffer_pos], cmd, cmd_len, 0); } else { /* When command compression is allowed and was ID of command changed? */ if( (cmd->id != last_cmd_id) || (last_cmd_count <= 0) ) { /* When this command is alone, then use default command size */ if(cmd_count == 0) { cmd_len = cmd_size; } else { /* FIXME: do not recompute command length here, but do it right, * when command is added to the queue */ cmd_len = v_cmds_len(cmd, cmd_count, cmd_share, cmd_len); } /* Debug print */ v_print_log(VRS_PRINT_DEBUG_MSG, "Cmd: %d, count: %d, length: %d\n", cmd->id, cmd_count, cmd_len); /* Add command to the buffer */ buffer_pos += v_cmd_pack(&io_ctx->buf[buffer_pos], cmd, cmd_len, cmd_share); /* Set up current count of commands in the line */ last_cmd_count = cmd_count; /* Update summary of commands length */ sum_len += cmd_len; } else { buffer_pos += v_cmd_pack(&io_ctx->buf[buffer_pos], cmd, 0, cmd_share); } } /* Print command */ v_cmd_print(VRS_PRINT_DEBUG_MSG, cmd); /* TODO: remove command alias here (layer value set/unset) */ /* Add command to the packet history */ ret = v_packet_history_add_cmd(&vconn->packet_history, sent_packet, cmd, prio); assert(ret==1); /* Update last command id */ last_cmd_id = cmd->id; /* Decrement counter of commands in queue */ last_cmd_count--; } } } return buffer_pos; }
/** * \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; }