Example #1
0
File: rate.c Project: edmonds/mtbl
void
rate_sleep(struct rate *r)
{
	struct timespec now, til;

	/* what clock to use depends on whether clock_nanosleep() is available */
#if HAVE_CLOCK_NANOSLEEP
	static const clockid_t rate_clock = CLOCK_MONOTONIC;
#else
	static const clockid_t rate_clock = CLOCK_REALTIME;
#endif

	if (r == NULL)
		return;

	/* update the event counter */
	r->count += 1;

	/* fetch the current time */
	clock_gettime(rate_clock, &now);

	/* special case: if this is the first call to rate_sleep(),
	 * calculate when the next tick will be. this is a little bit more
	 * accurate than calculating it in rate_init().
	 */
	if (r->count == 1) {
		r->start = now;
		r->next_tick = calc_next_tick(&now, &r->period);
	}

	/* adjust the rate and period every 'freq' events.
	 * skip the first window of 'freq' events.
	 * disabled if 'freq' is 0.
	 */
	if (r->freq != 0 && (r->count % r->freq) == 0 && r->count > r->freq)
		adjust_rate(r, &now);

	/* 'til', amount of time remaining until the next tick */
	til = r->next_tick;
	my_timespec_sub(&now, &til);

	/* if 'til' is in the past, don't bother sleeping */
	if (ts_nanos(&til) > 0) {
		/* do the sleep */
#if HAVE_CLOCK_NANOSLEEP
		clock_nanosleep(rate_clock, TIMER_ABSTIME, &r->next_tick, NULL);
#else
		struct timespec rel;
		rel = r->next_tick;
		my_timespec_sub(&now, &rel);
		my_nanosleep(&rel);
#endif

		/* re-fetch the current time */
		clock_gettime(rate_clock, &now);
	}

	/* calculate the next tick */
	r->next_tick = calc_next_tick(&now, &r->period);
}
Example #2
0
/**
 * Performs the Transfer phase for a particular file
 * Returns 1 if at least one client finished, 0 if all are dropped or aborted
 */
int transfer_phase(struct finfo_t *finfo)
{
    unsigned char *packet, *encpacket, *data;
    char path[MAXPATHNAME];
    struct uftp_h *header;
    struct fileseg_h *fileseg;
    int max_time, alldone, numbytes, sent_blocks, current_naks;
    unsigned int pass, section, numnaks, block;
    struct timeval start_time, last_sent, current_sent;
    int64_t avgwait, waitcnt, overage, tdiff;
    f_offset_t offset, curr_offset;
    int file, i;

    if (finfo->file_id != 0) {
        // First check to see if all clients are already done for this file.
        // This can happen on a restart when the file finished on the
        // last attempt and responded to the FILEINFO with a COMPLETE
        for (i = 0, alldone = 1; (i < destcount) && alldone; i++) {
            alldone = alldone && ((destlist[i].status == DEST_DONE) ||
                        (client_error(i)) || (destlist[i].clientcnt != -1));
        }
        if (alldone) {
            gettimeofday(&start_time, NULL);
            print_status(finfo, start_time);
            return 1;
        }
    }

    // If rate is -1, use 100Mbps for purpose of calculating max time
    max_time = (int)floor(((double)weight / 100) *
           ((double)finfo->size / (((rate == -1) ? 100000 : rate) / 8) / 1024));
    if (max_time < min_time) {
        max_time = min_time;
    }

    if ((finfo->file_id != 0) && (finfo->ftype == FTYPE_REG)) {
        log(0, 0, "Maximum file transfer time: %d seconds", max_time);
        snprintf(path, sizeof(path), "%s%c%s", finfo->basedir, PATH_SEP,
                                               finfo->filename);
        if ((file = open(path, OPENREAD, 0)) == -1) {
            syserror(0, 0, "Error opening file");
            return 1;
        }
    } else {
        // At end of group, all non-errored client are DEST_DONE from the
        // last file, so reset them to DEST_ACTIVE to get the final COMPLETE.
        for (i = 0; i < destcount; i++) {
            if (!client_error(i)) {
                destlist[i].status = DEST_ACTIVE;
            }
        }
    }

    packet = calloc(mtu, 1);
    encpacket = calloc(mtu, 1);
    if ((packet == NULL) || (encpacket == NULL)) {
        syserror(0, 0, "calloc failed!");
        exit(1);
    }
    header = (struct uftp_h *)packet;
    fileseg = (struct fileseg_h *)(packet + sizeof(struct uftp_h));
    data = (unsigned char *)fileseg + sizeof(struct fileseg_h);
    set_uftp_header(header, FILESEG, finfo, &receive_dest);    

    pass = 1;
    alldone = 0;
    gettimeofday(&start_time, NULL);
    do {
        avgwait = 0;
        waitcnt = 0;
        numnaks = 0;
        overage = 0;
        section = 1;
        curr_offset = 0;
        sent_blocks = 0;

        gettimeofday(&last_sent, NULL);
        if (finfo->file_id != 0) {
            log(0, 0, "Sending file...pass %d", pass);
            lseek_func(file, 0, SEEK_SET);
        } else {
            log(0, 0, "Finishing group");
        }
        fileseg->func = FILESEG;
        fileseg->file_id = htons(finfo->file_id);
        fileseg->pass = pass;
        fileseg->section = htons(section);
        for (block = 0; block < finfo->blocks; block++) {
            // If all clients received this file partially on a prior attempt
            // and it's the first pass, request NAKs for all sections
            // right away and don't send any data packets.
            if (((pass == 1) || finfo->naklist[block]) &&
                    !((pass == 1) && finfo->partial)) {
                if (diff_sec(last_sent, start_time) > max_time) {
                    log0(0, 0, "Max file transfer time exceeded");
                    send_abort(finfo, "Max file transfer time exceeded",
                            &receive_dest, NULL, (keytype != KEY_NONE),
                            !quit_on_error);
                    alldone = 1;
                    for (i = 0; i < destcount; i++) {
                        if (quit_on_error || ((destlist[i].status == DEST_ACTIVE) &&
                                               destlist[i].clientcnt == -1 )) {
                            destlist[i].status = DEST_ABORT;
                        }
                    }
                    break;
                }
                // On the first pass, go straight through the file.
                // On later passes, seek to the next packet.
                if (pass != 1) {
                    log4(0, 0, "Resending %d", block);
                    if (!seek_block(file, block, &offset, curr_offset)) {
                        continue;
                    }
                }
                if ((numbytes = read(file, data, blocksize)) == -1) {
                    syserror(0, 0, "read failed");
                    continue;
                }
                if (pass != 1) {
                    curr_offset = offset + numbytes;
                }

                // Keep track of how long we really slept compared to how
                // long we expected to sleep.  If we went over, subtract the
                // time over from the next sleep time.  This way we maintain
                // the proper average sleep time.  This can result in multiple
                // packets going out at once, potentially losing packets.
                if (packet_wait > overage) {
                    usleep(packet_wait - (int32_t)overage);
                }
                gettimeofday(&current_sent, NULL);
                tdiff = diff_usec(current_sent, last_sent);
                avgwait += tdiff;
                waitcnt++;
                if (packet_wait) overage += tdiff - packet_wait;
                last_sent = current_sent;

                fileseg->seq_num = htonl(block);
                send_data(finfo, packet, numbytes, encpacket);
                sent_blocks++;
            }
            if ((block % (blocksize * 8) == (blocksize * 8) - 1) ||
                    (block == finfo->blocks - 1)) {
                if ((pass == 1) || sent_blocks) {
                    current_naks = get_naks(finfo, pass, section, &alldone);
                    numnaks += current_naks;
                    if ((rate != -1) && (cc_count > 0)) {
                        if (!read_cc_config(cc_config)) {
                            log1(0, 0, "Error rereading congestion control "
                                       "config, using prior values");
                        }
                        adjust_rate(current_naks, sent_blocks);
                    }
                    overage = 0;
                    if (alldone) break;
                }
                sent_blocks = 0;
                gettimeofday(&last_sent, NULL);
                fileseg->section = htons(++section);
            }
        }
        if ((finfo->size == 0) && !alldone) {
            // If it's the end of the group, or an empty file, a DONE was
            // never sent, so send it now
            numnaks += get_naks(finfo, pass, section, &alldone);
        }
        if (finfo->file_id != 0) {
            log(0, 0, "Average wait time = %.2f us",
                       (waitcnt == 0) ? 0 : (float)avgwait / waitcnt);
            log(0, 0, "Received %d distinct NAKs for pass %d", numnaks, pass);
        }
        pass++;
    } while (!alldone);

    if ((finfo->file_id != 0) && (finfo->ftype == FTYPE_REG)) {
        close(file);
    }
    print_status(finfo, start_time);

    free(packet);
    free(encpacket);

    for (i = 0; i < destcount; i++) {
        if (quit_on_error) {
            // Check to see that all finished
            if ((destlist[i].status != DEST_DONE) &&
                    (destlist[i].clientcnt == -1)) {
                return 0;
            }
        } else {
            // Check to see if at least one finished
            if (destlist[i].status == DEST_DONE) {
                return 1;
            }
        }
    }
    if (quit_on_error) {
        return 1;
    } else {
        return 0;
    }
}