void handle_timedout_frames(Sender * sender, LLnode ** outgoing_frames_head_ptr) { //TODO: Suggested steps for handling timed out datagrams // 1) Iterate through the sliding window protocol information you maintain for each receiver // 2) Locate frames that are timed out and add them to the outgoing frames // 3) Update the next timeout field on the outgoing frames int i = 0; struct timeval *time = malloc(sizeof(struct timeval)); gettimeofday(time, NULL); for (;i < SWS; i++) { if (sender->sendQ[i].inuse == 1) { long timediff = timeval_usecdiff(time, sender->sendQ[i].timeout); if (timediff < 0) { Frame * outgoing_frame = (Frame *) sender->sendQ[i].msg; char * outgoing_msg = convert_frame_to_char(outgoing_frame); //fprintf(stderr, "%d frame timing out\n", outgoing_frame->seqNum); append_crc(outgoing_msg,MAX_FRAME_SIZE); ll_append_node(outgoing_frames_head_ptr, outgoing_msg); setTimeOutTime(sender->sendQ[i].timeout); //free(outgoing_frame); } } } }
void handle_timedout_frames(Sender * sender, LLnode ** outgoing_frames_head_ptr) { //Iterate through the send queue int i; for(i=0; i < glb_receivers_array_length; i++) { send_Q ** curr_node = sender->send_q_head[i]; int lar = sender->LAR[i]; int lfs = sender->LFS[i]; while(lar != lfs) { lar = (lar + 1) % (MAX_SEQ_NUM + 1); send_Q * curr = curr_node[lar%WS]; Frame * sent_frame = curr->frame; struct timeval * tv = curr->frame_timeout; struct timeval now; gettimeofday(&now, NULL); //A frame with set timeout is found if(tv != NULL) { //Update timeout field for old, timed out frames if(timeval_usecdiff(tv, &now) > 0) { fprintf(stderr, "TIMEDOUT DATA %s being resent\n", sent_frame->data); char * outframe_char_buf = convert_frame_to_char(sent_frame); ll_append_node(outgoing_frames_head_ptr, outframe_char_buf); calculate_timeout(curr->frame_timeout); } } //Update timeout field for fresh outgoing frames if(tv == NULL && sent_frame != NULL) { fprintf(stderr, "FRESH DATA %s being sent\n", sent_frame->data); curr->frame_timeout = (struct timeval *) malloc(sizeof(struct timeval)); calculate_timeout(curr->frame_timeout); } } } }
struct timeval * sender_get_next_expiring_timeval(Sender * sender) { struct timeval* tv = NULL; int i,j; //Iterate through the send queue for each receiver state for(i=0; i < glb_receivers_array_length; i++) { send_Q ** curr_node = sender->send_q_head[i]; for(j=0; j < WS; j++) { send_Q* curr = curr_node[j]; if(curr->frame_timeout == NULL) continue; //Found the next expiring timeval if(tv == NULL || timeval_usecdiff(tv, curr->frame_timeout) < 0) tv = curr->frame_timeout; } } return tv; }
void * run_sender(void * input_sender) { struct timespec time_spec; struct timeval curr_timeval; const int WAIT_SEC_TIME = 0; const long WAIT_USEC_TIME = 100000; Sender * sender = (Sender *) input_sender; LLnode * outgoing_frames_head; struct timeval * expiring_timeval; long sleep_usec_time, sleep_sec_time; //This incomplete sender thread, at a high level, loops as follows: //1. Determine the next time the thread should wake up //2. Grab the mutex protecting the input_cmd/inframe queues //3. Dequeues messages from the input queue and adds them to the outgoing_frames list //4. Releases the lock //5. Sends out the messages while(1) { outgoing_frames_head = NULL; //Get the current time gettimeofday(&curr_timeval, NULL); //time_spec is a data structure used to specify when the thread should wake up //The time is specified as an ABSOLUTE (meaning, conceptually, you specify 9/23/2010 @ 1pm, wakeup) time_spec.tv_sec = curr_timeval.tv_sec; time_spec.tv_nsec = curr_timeval.tv_usec * 1000; //Check for the next event we should handle expiring_timeval = sender_get_next_expiring_timeval(sender); //Perform full on timeout if (expiring_timeval == NULL) { time_spec.tv_sec += WAIT_SEC_TIME; time_spec.tv_nsec += WAIT_USEC_TIME * 1000; } else { //Take the difference between the next event and the current time sleep_usec_time = timeval_usecdiff(&curr_timeval, expiring_timeval); //Sleep if the difference is positive if (sleep_usec_time > 0) { sleep_sec_time = sleep_usec_time/1000000; sleep_usec_time = sleep_usec_time % 1000000; time_spec.tv_sec += sleep_sec_time; time_spec.tv_nsec += sleep_usec_time*1000; } } //Check to make sure we didn't "overflow" the nanosecond field if (time_spec.tv_nsec >= 1000000000) { time_spec.tv_sec++; time_spec.tv_nsec -= 1000000000; } //***************************************************************************************** //NOTE: Anything that involves dequeing from the input frames or input commands should go // between the mutex lock and unlock, because other threads CAN/WILL access these structures //***************************************************************************************** pthread_mutex_lock(&sender->buffer_mutex); //Check whether anything has arrived int input_cmd_length = ll_get_length(sender->input_cmdlist_head); int inframe_queue_length = ll_get_length(sender->input_framelist_head); //Nothing (cmd nor incoming frame) has arrived, so do a timed wait on the sender's condition variable (releases lock) //A signal on the condition variable will wakeup the thread and reaquire the lock if (input_cmd_length == 0 && inframe_queue_length == 0) { pthread_cond_timedwait(&sender->buffer_cv, &sender->buffer_mutex, &time_spec); } //Implement this handle_incoming_acks(sender, &outgoing_frames_head); //Implement this handle_input_cmds(sender, &outgoing_frames_head); pthread_mutex_unlock(&sender->buffer_mutex); //Implement this handle_timedout_frames(sender, &outgoing_frames_head); //CHANGE THIS AT YOUR OWN RISK! //Send out all the frames int ll_outgoing_frame_length = ll_get_length(outgoing_frames_head); while(ll_outgoing_frame_length > 0) { LLnode * ll_outframe_node = ll_pop_node(&outgoing_frames_head); char * char_buf = (char *) ll_outframe_node->value; //Don't worry about freeing the char_buf, the following function does that send_msg_to_receivers(char_buf); //Free up the ll_outframe_node free(ll_outframe_node); ll_outgoing_frame_length = ll_get_length(outgoing_frames_head); } } pthread_exit(NULL); return 0; }