/* $Id: mcl_time.cpp,v 1.2 2003/10/27 09:55:48 roca Exp $ */ /* * Copyright (c) 1999-2003 INRIA - Universite Paris 6 - All rights reserved * (main author: Vincent Roca - vincent.roca@inrialpes.fr) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. */ #include "mcl_includes.h" static int mcl_no_time_to_sleep = 0; /* if 1 then take measures */ /* * Timer thread, with a 1/MCL_TIMER_PERIOD fixed frequency * The mcl_time_count global variable, which reflects the nb of "ticks" * elapsed since the begining, is updated here. * * calls the various timer functions and the data sending function * NB: this thread is called with a mclcb param (the first one for which * we called mcl_init_timer) even if it is common to all sessions. * This is for conveniency only! */ void* mcl_timer_thread (void *arg) /* arg is first mclcb to call mcl_init_timer */ { int ses; /* session id */ mclcb_t *mclcb; /* and control block */ mcl_itime_t start_itime; /* to calculate exact time to sleep */ mcl_itime_t end_itime; /* to calculate exact time to sleep */ int dt; /* time diff in microseconds */ u_int dt2sleep; /* sleep that nb of microseconds */ int can_print_trc; /* print only one trace */ mcl_itime_t timer_thread_last_wakeup; /* last time timer_thread wakeup*/ float timer_thread_rem_tc; /* fractional part */ float true_tc; /* elapsed time_count as a float */ int tc; /* integral part of true_tc */ #ifdef GET_SYSINFO mclcb_t *mclcb_tmp = NULL;/* remember a valid mclcb */ int mcl_sysinfo_count = MCL_SYSINFO_PERIOD / MCL_TIMER_PERIOD; #endif mcl_lock(&mcl_mutex_lock); start_itime = mcl_get_itime(); timer_thread_last_wakeup = start_itime; timer_thread_rem_tc = 0.0; while (1) { /* * this sleep determines the timer granularity... * take into account the processing time spent in the * various calls to mcl_do_periodic_proc() */ end_itime = mcl_get_itime(); dt = mcl_it2usec(mcl_itime_sub(end_itime, start_itime)); /* don't sleep less than 0.1ms */ dt2sleep = max(100, (int)MCL_TIMER_PERIOD - dt); dt2sleep = min(MCL_TIMER_PERIOD, dt2sleep); mcl_no_time_to_sleep = 0; if (dt2sleep == 100) { mcl_no_time_to_sleep = 1; #ifdef DEBUG PRINT_ERR((mcl_stderr, "Timer thread: WARNING, tx rate too high, no time to sleep!\n")) #endif } mcl_unlock(&mcl_mutex_lock); mcl_usleep(dt2sleep); //mcl_usleep(MCL_TIMER_PERIOD); mcl_lock(&mcl_mutex_lock); start_itime = mcl_get_itime(); /* * update the global MCL time (in time ticks), the * mcl_time_count var. There is one tick every * MCL_TIMER_PERIOD micro-seconds */ true_tc = (float)mcl_it2usec(mcl_itime_sub(start_itime, timer_thread_last_wakeup)) / (float)MCL_TIMER_PERIOD + (float)timer_thread_rem_tc; tc = (int)(true_tc); mcl_time_count += tc; //printf("THREAD:: start_itime=%d, timer_thread_last_wakeup=%d, timer_thread_rem_tc=%f, true_tc=%f, mcl_time_count=%d\n", start_itime, timer_thread_last_wakeup, timer_thread_rem_tc, true_tc, mcl_time_count); timer_thread_last_wakeup = start_itime; timer_thread_rem_tc = true_tc - tc; /* * scan all the sessions */ can_print_trc = 1; for (ses = 0; ses < MCLCB_MAX_ID; ses++) { if ((mclcb = mclcbs[ses]) == NULL) continue; /* unused entry! */ if (!mclcb->initialized) continue; #ifdef DEBUG if (mclcb->verbose >= 6 && can_print_trc == 1) { can_print_trc = 0; /* just one trace! */ PRINT_OUT((mcl_stdout, "Timer thread %ld awoken, time_count=%d, ses_id=%d, state %s/%s\n", (ulong)mcl_timer_thread, mcl_time_count, ses, mcl_fsm_print_state(mclcb, SENDER), mcl_fsm_print_state(mclcb, RECEIVER))) } #endif /* DEBUG */ /* everything is done here... */ mcl_do_periodic_proc(mclcb); #ifdef GET_SYSINFO mclcb_tmp = mclcb; /* remember for GET_SYSINFO needs */ #endif } #ifdef GET_SYSINFO /* * print system information if required * precise timing is not required here... */ if (mclcb_tmp && mclcb_tmp->verbose == 2 && --mcl_sysinfo_count == 0) { /* one trace every approx. MCL_SYSINFO_PERIOD usec */ mcl_print_sysinfo(mclcb_tmp); mcl_sysinfo_count = MCL_SYSINFO_PERIOD / MCL_TIMER_PERIOD; } #endif /* GET_SYSINFO */ } mcl_unlock(&mcl_mutex_lock); arg = 0; #ifdef WIN32 ExitThread(0); #else pthread_exit(arg); #endif return arg; /* unused */ } /* * called periodically by mcl_timer_thread * can also be called by another function (while locked), eg. by mcl_send * to anticipate data transmissions while doing FEC pre-calculation */ void mcl_do_periodic_proc (mclcb_t *mclcb) { mcl_itime_t now; /* current itime */ mcl_itime_t elit; /* elasped itime since last call */ int eltc; /* elasped time_count since last call */ int i; float true_tx_tick_nb;/* tx tick elapsed as a float */ int tx_tick_nb; /* integral part of true_tx_tick_nb */ ASSERT(mclcb->initialized); TRACELVL(5, (mcl_stdout, "-> mcl_do_periodic_proc:\n")) now = mcl_get_itime(); if (mclcb->last_periodic_proc_tc == 0) { /* first call for this mclcb => timing initialization */ mclcb->last_periodic_proc_it = now; mclcb->last_periodic_proc_tc = mcl_time_count; mclcb->remaining_tx_tick_nb = 0.0; } /* * elapsed itime since last call */ elit = mcl_itime_sub(now, mclcb->last_periodic_proc_it); mclcb->last_periodic_proc_it = now; /* * elapsed time_count since last call * NB: no fractional part here, done in mcl_timer_thread */ eltc = mcl_time_count - mclcb->last_periodic_proc_tc; mclcb->last_periodic_proc_tc = mcl_time_count; #ifdef FLIDS /* * FLID-SL periodic processing */ if (mclcb->no_congestion_control == 0 && mclcb->sender && eltc > 0) { /* update counter */ mclcb->flids_cb.flids_tx_timer_count -= eltc; /* call flids_tx_timer as many times as required if eltc large*/ while (mclcb->flids_cb.flids_tx_timer_count < 0) { FLIDs_TxTimer(mclcb); mclcb->flids_cb.flids_tx_timer_count += mclcb->flids_cb.tsd / MCL_TIMER_PERIOD; } } #endif /* FLIDS */ #ifdef RLC /* * RLC periodic processing */ if (mclcb->no_congestion_control == 0 && mclcb->receiver && eltc > 0) { /* update counter */ mclcb->rlccb.rlc_rx_timer_count -= eltc; /* call rlc_rx_timer as many times as required if eltc large */ while (mclcb->rlccb.rlc_rx_timer_count < 0) { rlc_rx_timer(mclcb); mclcb->rlccb.rlc_rx_timer_count += mcl_rlc_rx_period / MCL_TIMER_PERIOD; } } #endif /* RLC */ /* * periodic transmissions */ if (mclcb->sender) { /* * how many tx ticks since last call? * first calculate the float tick nb, then its integral part, * and remember the decimal part for next time */ true_tx_tick_nb = (float)(mclcb->rate_l0 * mcl_it2sec(elit) / mclcb->txlvl_tab[0].du_per_tick + mclcb->remaining_tx_tick_nb); tx_tick_nb = (int)(true_tx_tick_nb); mclcb->remaining_tx_tick_nb = true_tx_tick_nb - (float)tx_tick_nb; if (mcl_no_time_to_sleep) { /* * NB: if in no_time_to_sleep mode, limit the * tx_tick_nb to try to limit the problem... */ tx_tick_nb = 1; } else { /* limit tx_tick_nb anyway to avoid large tx bursts * unless we are in singlelayer mode in which case * the number of packets may indeed be quite large * on the single transmission layer */ if (!(mclcb->single_layer_mode)) { tx_tick_nb = min(tx_tick_nb, 5); } } #ifdef NEVERDEF { struct timeval t; mcl_gettimeofday(&t); printf("time = (%d,%d)\n", (int)t.tv_sec, (int)t.tv_usec); } printf("eltc=%d, elit=%d, true_tx_tick_nb=%f, rate_l0=%d, elit(sec)=%f, remaining_tx_tick_nb=%f, tx_tick_nb=%d\n", eltc, elit, true_tx_tick_nb, mclcb->rate_l0, mcl_it2sec(elit), mclcb->remaining_tx_tick_nb, tx_tick_nb); #endif /* NEVERDEF */ /* call the tx function for each tx_tick */ for (i = tx_tick_nb; i > 0; i--) { mcl_try_to_send(mclcb); } } /* * launch memory cleanup function periodically */ if (mclcb->sender && mclcb->delivery_mode == DEL_MODE_STREAMING #ifdef VIRTUAL_TX_MEM && !(mclcb->vtm_used) #endif /* VIRTUAL_TX_MEM */ && eltc > 0) { /* update counter */ mclcb->tx_mem_cleanup_count -= eltc; if (mclcb->tx_mem_cleanup_count < 0) { mclcb->tx_mem_cleanup_count = TX_MEM_CLEANUP_PERIOD / MCL_TIMER_PERIOD; mcl_tx_cleanup(mclcb); } } // // mclcb->tx_mem_cleanup_rem_it = mcl_itime_sub(mclcb->tx_mem_cleanup_rem_it, elit); // if (mcl_itime_leq_0(mclcb->tx_mem_cleanup_rem_it)) { // if (mclcb->first_tx_for_mclcb == 0) { // /* clean only after tx start */ // mcl_tx_cleanup(mclcb); // } // mclcb->tx_mem_cleanup_rem_it = mcl_usec2it(TX_MEM_CLEANUP_PERIOD); // } /* * print stats periodically */ if (mclcb->statistics == 2) { mclcb->stats_time_count -= eltc; if (mclcb->stats_time_count < 0) { mclcb->stats_time_count = STATS_PERIOD / MCL_TIMER_PERIOD; if (mclcb->sender) mcl_print_tx_stats(mclcb); if (mclcb->receiver) mcl_print_rx_stats(mclcb); /* nb: stats_period is init'ed in func*/ } //mclcb->stat_rem_it = mcl_itime_sub(mclcb->stat_rem_it, elit); //if (mcl_itime_leq_0(mclcb->stat_rem_it)) { // mclcb->stat_rem_it = mcl_user2it(); // if (mclcb->sender) // mcl_print_tx_stats(mclcb); // if (mclcb->receiver) // mcl_print_rx_stats(mclcb); // /* nb: stats_period is init'ed in func*/ //} } TRACELVL(5, (mcl_stdout, "<- mcl_do_periodic_proc:\n")) } /****** mcl_itime functions ***************************************************/ /* * See mcl_time.h for internal MCL time representation */ #ifndef ITIME_FUNCTION_DEFINES mcl_itime_t mcl_usec2it (int from) { return((int)(from / 1000)); } int mcl_it2usec (mcl_itime_t from) { return(from * 1000); } float mcl_it2sec (mcl_itime_t from) { return((float)from * 0.001); } mcl_itime_t mcl_itime_sub (mcl_itime_t val, mcl_itime_t decr) { return(val - decr); } mcl_itime_t mcl_itime_add (mcl_itime_t val mcl_itime_t incr) { return(val + decr); } mcl_itime_t mcl_tv2it (struct timeval tv) { return(tv.tv_sec * 1000 + (long)((float)tv.tv_usec * 0.001)); } #endif /* !ITIME_FUNCTION_DEFINES */ /****** timeval functions *****************************************************/ /* * normalises a timeval struct * time in usec = sec * 1000000 + usec * normalised time is such that usec is in [0,999999] */ struct timeval mcl_norm_tvtime(struct timeval val) { while (val.tv_usec < 0) { val.tv_usec += 1000000; val.tv_sec--; } while (val.tv_usec >= 1000000) { val.tv_usec -= 1000000; val.tv_sec++; } return val; } /****** get time functions ****************************************************/ static int time_initialized = 0; static struct timeval tv0; /* initial timeval at initialization */ mcl_itime_t mcl_get_itime () { return(mcl_tv2it(mcl_get_tvtime())); } struct timeval mcl_get_tvtime () { struct timeval tv; if (!time_initialized) { time_initialized = 1; mcl_gettimeofday(&tv0); /*it0 = mcl_tv2it(tv0);*/ } mcl_gettimeofday(&tv); tv.tv_sec -= tv0.tv_sec; tv.tv_usec -= tv0.tv_usec; return mcl_norm_tvtime(tv); }