al_bp_error_t prepare_force_stop_bundle(al_bp_bundle_object_t * start, al_bp_endpoint_id_t monitor, al_bp_timeval_t expiration, al_bp_bundle_priority_t priority) { FILE * start_stream; HEADER_TYPE start_header = FORCE_STOP_HEADER; al_bp_endpoint_id_t none; al_bp_bundle_delivery_opts_t opts = BP_DOPTS_NONE; al_bp_bundle_set_payload_location(start, BP_PAYLOAD_MEM); open_payload_stream_write(*start, &start_stream); fwrite(&start_header, HEADER_SIZE, 1, start_stream); close_payload_stream_write(start, start_stream); al_bp_bundle_set_dest(start, monitor); al_bp_get_none_endpoint(&none); al_bp_bundle_set_replyto(start, none); al_bp_bundle_set_delivery_opts(start, opts); al_bp_bundle_set_expiration(start, expiration); al_bp_bundle_set_priority(start, priority); return BP_SUCCESS; }
al_bp_error_t prepare_stop_bundle(al_bp_bundle_object_t * stop, al_bp_endpoint_id_t monitor, al_bp_timeval_t expiration, al_bp_bundle_priority_t priority, int sent_bundles) { FILE * stop_stream; HEADER_TYPE stop_header = STOP_HEADER; al_bp_endpoint_id_t none; uint32_t buf; al_bp_bundle_delivery_opts_t opts = BP_DOPTS_NONE; al_bp_bundle_set_payload_location(stop, BP_PAYLOAD_MEM); open_payload_stream_write(*stop, &stop_stream); fwrite(&stop_header, HEADER_SIZE, 1, stop_stream); buf = htonl(sent_bundles); fwrite(&buf, sizeof(buf), 1, stop_stream); close_payload_stream_write(stop, stop_stream); al_bp_bundle_set_dest(stop, monitor); al_bp_get_none_endpoint(&none); al_bp_bundle_set_replyto(stop, none); al_bp_bundle_set_delivery_opts(stop, opts); al_bp_bundle_set_expiration(stop, expiration); al_bp_bundle_set_priority(stop, priority); return BP_SUCCESS; }
/* ---------------------------- * CLIENT CODE * ---------------------------- */ void run_dtnperf_client(dtnperf_global_options_t * perf_g_opt) { /* ------------------------ * variables * ------------------------ */ char * client_demux_string; int pthread_status; char temp1[256]; // buffer for different purpose char temp2[256]; FILE * stream; // stream for preparing payolad bp_bundle_object_t bundle_stop; monitor_parameters_t mon_params; /* ------------------------ * initialize variables * ------------------------ */ perf_opt = perf_g_opt->perf_opt; conn_opt = perf_g_opt->conn_opt; boolean_t debug = perf_opt->debug; int debug_level = perf_opt->debug_level; boolean_t verbose = perf_opt->verbose; boolean_t create_log = perf_opt->create_log; log_open = FALSE; bp_handle_open = FALSE; source_file_created = FALSE; stream = NULL; tot_bundles = 0; process_interrupted = FALSE; perf_opt->log_filename = correct_dirname(perf_opt->log_filename); source_file = (char*) malloc(strlen(SOURCE_FILE) + 7); sprintf(source_file, "%s_%d", SOURCE_FILE, getpid()); // Create a new log file if (create_log) { if ((log_file = fopen(perf_opt->log_filename, "w")) == NULL) { fprintf(stderr, "fatal error opening log file\n"); client_clean_exit(1); } log_open = TRUE; } // Connect to BP Daemon if ((debug) && (debug_level > 0)) printf("[debug] opening connection to local BP daemon..."); if (perf_opt->use_ip) error = bp_open_with_ip(perf_opt->ip_addr,perf_opt->ip_port,&handle); else error = bp_open(&handle); if (error != BP_SUCCESS) { fprintf(stderr, "fatal error opening bp handle: %s\n", bp_strerror(error)); if (create_log) fprintf(log_file, "fatal error opening bp handle: %s\n", bp_strerror(error)); client_clean_exit(1); } else { bp_handle_open = TRUE; } if ((debug) && (debug_level > 0)) printf("done\n"); // Ctrl+C handler signal(SIGINT, &client_handler); /* ----------------------------------------------------- * initialize and parse bundle src/dest/replyto EIDs * ----------------------------------------------------- */ // append process id to the client demux string client_demux_string = malloc (strlen(CLI_EP_STRING) + 10); sprintf(client_demux_string, "%s_%d", CLI_EP_STRING, getpid()); //build a local eid if(debug && debug_level > 0) printf("[debug] building a local eid..."); bp_build_local_eid(handle, &local_eid, client_demux_string); if(debug && debug_level > 0) printf("done\n"); if (debug) printf("Source : %s\n", local_eid.uri); if (create_log) fprintf(log_file, "\nSource : %s\n", local_eid.uri); // parse SERVER EID // append server demux string to destination eid strcat(perf_opt->dest_eid, SERV_EP_STRING); if (verbose) fprintf(stdout, "%s (local)\n", perf_opt->dest_eid); // parse error = bp_parse_eid_string(&dest_eid, perf_opt->dest_eid); if (error != BP_SUCCESS) { fprintf(stderr, "fatal error parsing bp EID: invalid eid string '%s'\n", perf_opt->dest_eid); if (create_log) fprintf(log_file, "\nfatal error parsing bp EID: invalid eid string '%s'", perf_opt->dest_eid); client_clean_exit(1); } if (debug) printf("Destination: %s\n", dest_eid.uri); if (create_log) fprintf(log_file, "Destination: %s\n", dest_eid.uri); // parse REPLY-TO (if none specified, same as the source) if (strlen(perf_opt->mon_eid) == 0) { char * ptr; ptr = strstr(local_eid.uri, CLI_EP_STRING); // copy from local eid only the uri (not the demux string) strncpy(perf_opt->mon_eid, local_eid.uri, ptr - local_eid.uri); } // append monitor demux string to replyto eid strcat(perf_opt->mon_eid, MON_EP_STRING); // parse error = bp_parse_eid_string(&mon_eid, perf_opt->mon_eid); if (error != BP_SUCCESS) { fprintf(stderr, "fatal error parsing bp EID: invalid eid string '%s'\n", perf_opt->dest_eid); if (create_log) fprintf(log_file, "\nfatal error parsing bp EID: invalid eid string '%s'", perf_opt->dest_eid); client_clean_exit(1); } if (debug) printf("Reply-to : %s\n\n", mon_eid.uri); if (create_log) fprintf(log_file, "Reply-to : %s\n\n", mon_eid.uri); if(create_log) fflush(log_file); // checking if there is a running monitor on this endpoint if(perf_g_opt->mode == DTNPERF_CLIENT_MONITOR) { if(debug && debug_level > 0) printf("[debug] checking for existing monitor on this endpoint...\n"); error = bp_find_registration(handle, &mon_eid, ®id); if (error == BP_SUCCESS) { dedicated_monitor = FALSE; printf("there is already a monitor on this endpoint.\n"); printf("regid 0x%x\n", (unsigned int) regid); } else { dedicated_monitor = TRUE; mon_params.client_id = getpid(); mon_params.perf_g_opt = perf_g_opt; printf("there is not a monitor on this endpoint.\n"); sprintf(temp1, "%s_%d", mon_eid.uri, mon_params.client_id); bp_parse_eid_string(&mon_eid, temp1); // start dedicated monitor if ((monitor_pid = fork()) == 0) { start_dedicated_monitor((void *) &mon_params); exit(0); } printf("started a new dedicated monitor\n"); } if ((debug) && (debug_level > 0)) printf(" done\n"); } //create a new registration to the local router based on this eid if(debug && debug_level > 0) printf("[debug] registering to local daemon..."); memset(®info, 0, sizeof(reginfo)); bp_copy_eid(®info.endpoint, &local_eid); reginfo.flags = BP_REG_DEFER; reginfo.regid = BP_REGID_NONE; reginfo.expiration = 0; if ((error = bp_register(handle, ®info, ®id)) != 0) { fflush(stdout); fprintf(stderr, "error creating registration: %d (%s)\n", error, bp_strerror(bp_errno(handle))); if (create_log) fprintf(log_file, "error creating registration: %d (%s)\n", error, bp_strerror(bp_errno(handle))); client_clean_exit(1); } if ((debug) && (debug_level > 0)) printf(" done\n"); if (debug) printf("regid 0x%x\n", (unsigned int) regid); if (create_log) fprintf(log_file, "regid 0x%x\n", (unsigned int) regid); // if bundle payload > MAX_MEM_PAYLOAD, then transfer a file if (!perf_opt->use_file && perf_opt->bundle_payload > MAX_MEM_PAYLOAD) { perf_opt->use_file = 1; perf_opt->bundle_payload = BP_PAYLOAD_FILE; if (verbose) printf("Payload %ld > %d: Using file instead of memory\n", perf_opt->bundle_payload, MAX_MEM_PAYLOAD); if (create_log) fprintf(log_file, "Payload %ld > %d: Using file instead of memory\n", perf_opt->bundle_payload, MAX_MEM_PAYLOAD); } /* ------------------------------------------------------------------------------ * select the operative-mode (between Time_Mode, Data_Mode and File_Mode) * ------------------------------------------------------------------------------ */ if (perf_opt->op_mode == 'T') // Time mode { if (verbose) printf("Working in Time_Mode\n"); if (create_log) fprintf(log_file, "Working in Time_Mode\n"); if (verbose) printf("requested %d second(s) of transmission\n", perf_opt->transmission_time); if (create_log) fprintf(log_file, "requested %d second(s) of transmission\n", perf_opt->transmission_time); } else if (perf_opt->op_mode == 'D') // Data mode { if (verbose) printf("Working in Data_Mode\n"); if (create_log) fprintf(log_file, "Working in Data_Mode\n"); if (verbose) printf("requested transmission of %ld bytes of data\n", perf_opt->data_qty); if (create_log) fprintf(log_file, "requested transmission of %ld bytes of data\n", perf_opt->data_qty); } else if (perf_opt->op_mode == 'F') // File mode { if (verbose) printf("Working in File_Mode\n"); if (create_log) fprintf(log_file, "Working in File_Mode\n"); if (verbose) printf("requested transmission of file %s\n", perf_opt->F_arg); if (create_log) fprintf(log_file, "requested transmission of file %s\n", perf_opt->F_arg); } if (verbose) printf(" transmitting data %s\n", perf_opt->use_file ? "using a file" : "using memory"); if (create_log) fprintf(log_file, " transmitting data %s\n", perf_opt->use_file ? "using a file" : "using memory"); if (verbose) printf("%s based congestion control:\n", perf_opt->congestion_ctrl == 'w' ? "sliding window" : "rate"); if (create_log) fprintf(log_file, "%s based congestion control:\n", perf_opt->congestion_ctrl == 'w' ? "sliding window" : "rate"); if(perf_opt->congestion_ctrl == 'w') { if (verbose) printf("\twindow is %d bundles\n", perf_opt->window); if (create_log) fprintf(log_file, "\twindow is %d bundles\n", perf_opt->window); } else { if (verbose) printf("\trate is %ld %c\n", perf_opt->rate, perf_opt->rate_unit); if (create_log) fprintf(log_file, "\trate is %ld %c\n", perf_opt->rate, perf_opt->rate_unit); } if (verbose) printf("payload is %ld bytes\n", perf_opt->bundle_payload); if (create_log) fprintf(log_file, "payload is %ld bytes\n", perf_opt->bundle_payload); sent_bundles = 0; if (perf_opt->op_mode == 'D' || perf_opt->op_mode == 'F') // Data or File mode { if ((debug) && (debug_level > 0)) printf("[debug] calculating how many bundles are needed..."); if (perf_opt->op_mode == 'F') // File mode { struct stat file; if (stat(perf_opt->F_arg, &file) < 0) { fprintf(stderr, "couldn't stat file %s : %s", perf_opt->F_arg, strerror(errno)); if (create_log) fprintf(log_file, "couldn't stat file %s : %s", perf_opt->F_arg, strerror(errno)); client_clean_exit(1); } // get transfer file basename strcpy(temp1, perf_opt->F_arg); strcpy(temp2, basename(temp1)); transfer_filename = malloc(strlen(temp2) + 1); strcpy(transfer_filename, temp2); transfer_filedim = file.st_size; tot_bundles += bundles_needed(transfer_filedim, get_file_fragment_size(perf_opt->bundle_payload, strlen(transfer_filename))); } else // Data mode tot_bundles += bundles_needed(perf_opt->data_qty, perf_opt->bundle_payload); if ((debug) && (debug_level > 0)) printf(" n_bundles = %ld\n", tot_bundles); } // Create the file if (perf_opt->use_file) { // create the file if ((debug) && (debug_level > 0)) printf("[debug] creating file %s...", source_file); stream = fopen(source_file, "wb"); if (stream == NULL) { fprintf(stderr, "ERROR: couldn't create file %s.\n \b Maybe you don't have permissions\n", source_file); if (create_log) fprintf(log_file, "ERROR: couldn't create file %s.\n \b Maybe you don't have permissions\n", source_file); client_clean_exit(2); } source_file_created = TRUE; fclose(stream); if ((debug) && (debug_level > 0)) printf(" done\n"); // set the absolute path of the source file char buf[256]; getcwd(buf, 256); strcat(buf, "/"); strcat(buf, source_file); source_file_abs = malloc(strlen(buf) + 1); strncpy(source_file_abs, buf, strlen(buf) + 1); } // Create the bundle object if ((debug) && (debug_level > 0)) printf("[debug] creating the bundle object..."); error = bp_bundle_create(& bundle); if (error != BP_SUCCESS) { fprintf(stderr, "ERROR: couldn't create bundle object\n"); if (create_log) fprintf(log_file, "ERROR: couldn't create bundle object\n"); client_clean_exit(1); } if ((debug) && (debug_level > 0)) printf(" done\n"); // Fill the payload if ((debug) && (debug_level > 0)) printf("[debug] filling payload..."); if (perf_opt->use_file) error = bp_bundle_set_payload_file(&bundle, source_file_abs, strlen(source_file_abs)); else error = bp_bundle_set_payload_mem(&bundle, buffer, bufferLen); if (error != BP_SUCCESS) { fprintf(stderr, "ERROR: couldn't set bundle payload\n"); if (create_log) fprintf(log_file, "ERROR: couldn't set bundle payload\n"); client_clean_exit(1); } if ((debug) && (debug_level > 0)) printf(" done\n"); // open payload stream in write mode if (open_payload_stream_write(bundle, &stream) < 0) { fprintf(stderr, "ERROR: couldn't open payload stream in write mode"); if (create_log) fprintf(log_file, "ERROR: couldn't open payload stream in write mode"); client_clean_exit(2); } // prepare the payload if(perf_opt->op_mode == 'F') // File mode { // payload will be prepared into send_bundles() cycle // open file to transfer in read mode if ((transfer_fd = open(perf_opt->F_arg, O_RDONLY)) < 0) { fprintf(stderr, "couldn't stat file %s : %s", perf_opt->F_arg, strerror(errno)); if (create_log) fprintf(log_file, "couldn't stat file %s : %s", perf_opt->F_arg, strerror(errno)); client_clean_exit(2); } } else // Time and Data mode { error = prepare_generic_payload(perf_opt, stream, perf_opt->bundle_payload); if (error != BP_SUCCESS) { fprintf(stderr, "error preparing payload: %s\n", bp_strerror(error)); if (create_log) fprintf(log_file, "error preparing payload: %s\n", bp_strerror(error)); client_clean_exit(1); } } // close the stream close_payload_stream_write(&bundle, stream); if(debug) printf("[debug] payload prepared"); // Create the array for the bundle send info (only for sliding window congestion control) if (perf_opt->congestion_ctrl == 'w') { if ((debug) && (debug_level > 0)) printf("[debug] creating structure for sending information..."); send_info = (send_information_t*) malloc(perf_opt->window * sizeof(send_information_t)); init_info(send_info, perf_opt->window); if ((debug) && (debug_level > 0)) printf(" done\n"); } // Setting the bundle options bp_bundle_set_source(&bundle, local_eid); bp_bundle_set_dest(&bundle, dest_eid); bp_bundle_set_replyto(&bundle, mon_eid); set_bp_options(&bundle, conn_opt); // intialize stop bundle; bp_bundle_create(&bundle_stop); if ((debug) && (debug_level > 0)) printf("[debug] entering in loop\n"); // Run threads if (perf_opt->congestion_ctrl == 'w') // sliding window congestion control sem_init(&window, 0, perf_opt->window); else // rate based congestion control sem_init(&window, 0, 0); sigset_t sigset; // blocking signals for the threads sigemptyset(&sigset); sigaddset(&sigset, SIGINT); sigaddset(&sigset, SIGUSR1); sigaddset(&sigset, SIGUSR2); pthread_sigmask(SIG_BLOCK, &sigset, NULL); pthread_cond_init(&cond_ackreceiver, NULL); pthread_mutex_init (&mutexdata, NULL); pthread_create(&sender, NULL, send_bundles, (void*)perf_g_opt); pthread_create(&cong_ctrl, NULL, congestion_control, (void*)perf_g_opt); pthread_create(&wait_for_signal, NULL, wait_for_sigint, (void*) client_demux_string); pthread_join(cong_ctrl, (void**)&pthread_status); pthread_join(sender, (void**)&pthread_status); pthread_mutex_destroy(&mutexdata); sem_destroy(&window); pthread_cond_destroy(&cond_ackreceiver); // if user sent Ctrl+C to the client, // let the wait_for_signal thread to terminate the execution if (process_interrupted) pause(); if ((debug) && (debug_level > 0)) printf("[debug] out from loop\n"); // Get the TOTAL end time if ((debug) && (debug_level > 0)) printf("[debug] getting total end-time..."); gettimeofday(&end, NULL); if ((debug) && (debug_level > 0)) printf(" end.tv_sec = %u sec\n", (u_int)end.tv_sec); // Print final report print_final_report(NULL); if(perf_opt->create_log) print_final_report(log_file); // fill the stop bundle prepare_stop_bundle(&bundle_stop, mon_eid, conn_opt->expiration, conn_opt->priority, sent_bundles); bp_bundle_set_source(&bundle_stop, local_eid); // send stop bundle to monitor if (debug) printf("sending the stop bundle to the monitor..."); if ((error = bp_bundle_send(handle, regid, &bundle_stop)) != 0) { fprintf(stderr, "error sending the stop bundle: %d (%s)\n", error, bp_strerror(error)); if (create_log) fprintf(log_file, "error sending the stop bundle: %d (%s)\n", error, bp_strerror(error)); client_clean_exit(1); } if (debug) printf("done.\n"); // waiting monitor stops if (dedicated_monitor) { printf("\nWaiting for dedicated monitor to stop...\n"); wait(&monitor_status); } // Close the BP handle -- if ((debug) && (debug_level > 0)) printf("[debug] closing DTN handle..."); if (bp_close(handle) != BP_SUCCESS) { fprintf(stderr, "fatal error closing bp handle: %s\n", strerror(errno)); if (create_log) fprintf(log_file, "fatal error closing bp handle: %s\n", strerror(errno)); client_clean_exit(1); } else { bp_handle_open = FALSE; } if ((debug) && (debug_level > 0)) printf(" done\n"); if (create_log) { fclose(log_file); log_open = FALSE; } // deallocate memory if (perf_opt->op_mode == 'F') { close(transfer_fd); } if (perf_opt->use_file) { remove(source_file); source_file_created = FALSE; if (debug && debug > 1) { printf("[debug] removed file %s\n", source_file); } } free((void*)buffer); free(client_demux_string); free(source_file_abs); free(source_file); free(transfer_filename); free(send_info); bp_bundle_free(&bundle); bp_bundle_free(&bundle_stop); if (perf_opt->create_log) printf("\nClient log saved: %s\n", perf_opt->log_filename); printf("\n"); exit(0); }
/** * Client Threads code */ void * send_bundles(void * opt) { dtnperf_options_t *perf_opt = ((dtnperf_global_options_t *)(opt))->perf_opt; boolean_t debug = perf_opt->debug; int debug_level = perf_opt->debug_level; boolean_t create_log = perf_opt->create_log; boolean_t condition; boolean_t eof_reached; u32_t actual_payload; FILE * stream; // Initialize timer if ((debug) && (debug_level > 0)) printf("[debug send thread] initializing timer..."); if (create_log) fprintf(log_file, " initializing timer..."); gettimeofday(&start, NULL); if ((debug) && (debug_level > 0)) printf(" start.tv_sec = %d sec\n", (u_int)start.tv_sec); if (create_log) fprintf(log_file, " start.tv_sec = %d sec\n", (u_int)start.tv_sec); if (perf_opt->op_mode == 'T') // TIME MODE { // Calculate end-time if ((debug) && (debug_level > 0)) printf("[debug send thread] calculating end-time..."); if (create_log) fprintf(log_file, " calculating end-time..."); end = set (0); end.tv_sec = start.tv_sec + perf_opt->transmission_time; if ((debug) && (debug_level > 0)) printf(" end.tv_sec = %d sec\n", (u_int)end.tv_sec); if (create_log) fprintf(log_file, " end.tv_sec = %d sec\n", (u_int)end.tv_sec); } if ((debug) && (debug_level > 0)) printf("[debug send thread] entering loop...\n"); if (create_log) fprintf(log_file, " entering loop...\n"); if (perf_opt->op_mode == 'T') // TIME MODE { // init variables for loop and setting condition now.tv_sec = start.tv_sec; condition = now.tv_sec <= end.tv_sec; } else // DATA and FILE MODE { // setting condition for loop condition = sent_bundles < tot_bundles; } // send bundles loop while (condition) //LOOP { // prepare payload if FILE MODE if (perf_opt->op_mode == 'F') { open_payload_stream_write(bundle, &stream); error = prepare_file_transfer_payload(perf_opt, stream, transfer_fd, transfer_filename, transfer_filedim, &eof_reached); close_payload_stream_write(&bundle, stream); } // prepare payload if last bundle of DATA MODE if (perf_opt->op_mode == 'D') { if (sent_bundles == tot_bundles - 1 && perf_opt->data_qty % perf_opt->bundle_payload != 0) { open_payload_stream_write(bundle, &stream); prepare_generic_payload(perf_opt, stream, perf_opt->data_qty % perf_opt->bundle_payload); close_payload_stream_write(&bundle, stream); } } // window debug if ((debug) && (debug_level > 1)) { int cur; sem_getvalue(&window, &cur); printf("\t[debug send thread] window is %d\n", cur); } // wait for the semaphore sem_wait(&window); if (perf_opt->op_mode == 'T') // TIME MODE { // force re-check of condition after the semaphore gettimeofday(&now, NULL); if (now.tv_sec > end.tv_sec) break; } // Send the bundle if (debug) printf("sending the bundle..."); if (perf_opt->congestion_ctrl == 'w') pthread_mutex_lock(&mutexdata); if ((error = bp_bundle_send(handle, regid, &bundle)) != 0) { fprintf(stderr, "error sending bundle: %d (%s)\n", error, bp_strerror(error)); if (create_log) fprintf(log_file, "error sending bundle: %d (%s)\n", error, bp_strerror(error)); client_clean_exit(1); } if ((error = bp_bundle_get_id(bundle, &bundle_id)) != 0) { fprintf(stderr, "error getting bundle id: %s\n", bp_strerror(error)); if (create_log) fprintf(log_file, "error getting bundle id: %s\n", bp_strerror(error)); client_clean_exit(1); } if (debug) printf(" bundle sent\n"); if ((debug) && (debug_level > 0)) printf("\t[debug send thread] "); printf("bundle sent timestamp: %llu.%llu\n", (unsigned long long) bundle_id->creation_ts.secs, (unsigned long long) bundle_id->creation_ts.seqno); if (create_log) fprintf(log_file, "\t bundle sent timestamp: %llu.%llu\n", (unsigned long long) bundle_id->creation_ts.secs, (unsigned long long) bundle_id->creation_ts.seqno); // put bundle id in send_info (only windowed congestion control) if (perf_opt->congestion_ctrl == 'w') { gettimeofday(&bundle_sent, NULL); add_info(send_info, *bundle_id, bundle_sent, perf_opt->window); if ((debug) && (debug_level > 1)) printf("\t[debug send thread] added info for sent bundle\n"); pthread_cond_signal(&cond_ackreceiver); pthread_mutex_unlock(&mutexdata); } // Increment sent_bundles ++sent_bundles; if ((debug) && (debug_level > 0)) printf("\t[debug send thread] now bundles_sent is %d\n", sent_bundles); if (create_log) fprintf(log_file, "\t now bundles_sent is %d\n", sent_bundles); // Increment data_qty bp_bundle_get_payload_size(bundle, &actual_payload); sent_data += actual_payload; if (perf_opt->op_mode == 'T') // TIME MODE { // update time and condition gettimeofday(&now, NULL); condition = now.tv_sec <= end.tv_sec; } else // DATA MODE { // update condition condition = sent_bundles < tot_bundles; } } // while if ((debug) && (debug_level > 0)) printf("[debug send thread] ...out from loop\n"); if (create_log) fprintf(log_file, " ...out from loop\n"); pthread_mutex_lock(&mutexdata); close_ack_receiver = 1; if (perf_opt->congestion_ctrl == 'r') { // terminate congestion control thread pthread_cancel(cong_ctrl); } else { pthread_cond_signal(&cond_ackreceiver); } pthread_mutex_unlock(&mutexdata); // close thread pthread_exit(NULL); }