diff --git a/networktransport.hpp b/networktransport.hpp index 7c2adaf..2581686 100644 --- a/networktransport.hpp +++ b/networktransport.hpp @@ -77,8 +77,6 @@ namespace Network { int fd( void ) { return connection.fd(); } void set_verbose( void ) { sender.set_verbose(); verbose = true; } - - uint64_t timeout( void ) { return connection.timeout(); } }; } diff --git a/stmclient.cpp b/stmclient.cpp index e05113e..1b450c0 100644 --- a/stmclient.cpp +++ b/stmclient.cpp @@ -164,8 +164,7 @@ bool STMClient::process_user_input( int fd ) for ( int i = 0; i < bytes_read; i++ ) { char the_byte = buf[ i ]; - overlays.get_prediction_engine().new_user_byte( the_byte, *local_framebuffer, - network->timeout() ); + overlays.get_prediction_engine().new_user_byte( the_byte, *local_framebuffer ); if ( quit_sequence_started ) { if ( the_byte == '.' ) { /* Quit sequence is Ctrl-^ . */ diff --git a/terminaloverlay.cpp b/terminaloverlay.cpp index 45122c9..376d420 100644 --- a/terminaloverlay.cpp +++ b/terminaloverlay.cpp @@ -92,10 +92,29 @@ void OverlayEngine::apply( Framebuffer &fb ) const [&fb]( OverlayElement *x ) { x->apply( fb ); } ); } -void OverlayEngine::cull( const Framebuffer &fb ) +void PredictionEngine::cull( const Framebuffer &fb ) { + uint64_t now = timestamp(); + auto i = elements.begin(); while ( i != elements.end() ) { + /* update echo timeout state */ + if ( (*i)->get_validity( fb ) == Correct ) { + double R = now - (*i)->prediction_time; + if ( !RTT_hit ) { /* first measurement */ + SRTT = R; + RTTVAR = R / 2; + RTT_hit = true; + } else { + const double alpha = 1.0 / 8.0; + const double beta = 1.0 / 4.0; + + RTTVAR = (1 - beta) * RTTVAR + ( beta * fabs( SRTT - R ) ); + SRTT = (1 - alpha) * SRTT + ( alpha * R ); + } + } + + /* eliminate predictions proven correct or incorrect */ if ( (*i)->get_validity( fb ) != Pending ) { delete (*i); i = elements.erase( i ); @@ -263,6 +282,8 @@ void NotificationEngine::apply( Framebuffer &fb ) const void OverlayManager::apply( Framebuffer &fb ) { predictions.calculate_score( fb ); + + /* eliminate predictions proven correct or incorrect and update echo timers */ predictions.cull( fb ); if ( predictions.get_score() > 3 ) { @@ -289,14 +310,14 @@ void PredictionEngine::calculate_score( const Framebuffer &fb ) } } -void PredictionEngine::new_user_byte( char the_byte, const Framebuffer &fb, int prediction_len ) +void PredictionEngine::new_user_byte( char the_byte, const Framebuffer &fb ) { uint64_t now = timestamp(); if ( elements.empty() ) { /* starting from scratch */ - elements.push_front( new ConditionalCursorMove( now + prediction_len, + elements.push_front( new ConditionalCursorMove( now + prediction_len(), fb.ds.get_cursor_row(), fb.ds.get_cursor_col() ) ); } @@ -313,7 +334,7 @@ void PredictionEngine::new_user_byte( char the_byte, const Framebuffer &fb, int const Cell *existing_cell = fb.get_cell( ccm->new_row, ccm->new_col ); - ConditionalOverlayCell *coc = new ConditionalOverlayCell( now + prediction_len, + ConditionalOverlayCell *coc = new ConditionalOverlayCell( now + prediction_len(), ccm->new_row, ccm->new_col, existing_cell->renditions.background_color, *existing_cell ); @@ -323,7 +344,7 @@ void PredictionEngine::new_user_byte( char the_byte, const Framebuffer &fb, int coc->replacement.contents.push_back( the_byte ); ccm->new_col++; - ccm->expiration_time = now + prediction_len; + ccm->expiration_time = now + prediction_len(); elements.push_back( coc ); } else { @@ -357,3 +378,14 @@ int OverlayManager::wait_time( void ) return ret; } } + +int PredictionEngine::prediction_len( void ) +{ + uint64_t RTO = lrint( ceil( SRTT + 4 * RTTVAR ) ); + if ( RTO < 20 ) { + RTO = 20; + } else if ( RTO > 2000 ) { + RTO = 2000; + } + return RTO; +} diff --git a/terminaloverlay.hpp b/terminaloverlay.hpp index a410d91..d1f0be0 100644 --- a/terminaloverlay.hpp +++ b/terminaloverlay.hpp @@ -20,12 +20,13 @@ namespace Overlay { /* The individual elements of an overlay -- cursor movements and replaced cells */ class OverlayElement { public: - uint64_t expiration_time; + uint64_t prediction_time, expiration_time; virtual void apply( Framebuffer &fb ) const = 0; virtual Validity get_validity( const Framebuffer & ) const; - OverlayElement( uint64_t s_expiration_time ) : expiration_time( s_expiration_time ) {} + OverlayElement( uint64_t s_expiration_time ) : prediction_time( timestamp() ), + expiration_time( s_expiration_time ) {} virtual ~OverlayElement() {} }; @@ -75,7 +76,6 @@ namespace Overlay { list elements; public: - void cull( const Framebuffer &fb ); virtual void apply( Framebuffer &fb ) const; void clear( void ); @@ -109,11 +109,18 @@ namespace Overlay { private: int score; + /* use the TCP timeout algorithm to measure appropriate echo prediction timeout */ + bool RTT_hit; + double SRTT, RTTVAR; + int prediction_len( void ); + public: - void new_user_byte( char the_byte, const Framebuffer &fb, int prediction_len ); + void cull( const Framebuffer &fb ); + void new_user_byte( char the_byte, const Framebuffer &fb ); void calculate_score( const Framebuffer &fb ); - PredictionEngine() : score( 0 ) {} + PredictionEngine() : score( 0 ), RTT_hit( false ), SRTT( 1000 ), RTTVAR( 500 ) {} + int get_score( void ) { return score; } };