]> granicus.if.org Git - transmission/commitdiff
(trunk libT) #2584 "Checks for AltTime (turtle mode) start and stop and may be missed...
authorCharles Kerr <charles@transmissionbt.com>
Thu, 4 Feb 2010 23:39:50 +0000 (23:39 +0000)
committerCharles Kerr <charles@transmissionbt.com>
Thu, 4 Feb 2010 23:39:50 +0000 (23:39 +0000)
libtransmission/session.c
libtransmission/session.h

index 95aada4b872104dd5cc0ea8869c04e8b2e8cb9a4..00810ee06d15c15b58052f437b1b47cd4189a542 100644 (file)
@@ -225,43 +225,6 @@ tr_sessionGetPublicAddress( const tr_session * session, int tr_af_type )
 ****
 ***/
 
-static tr_bool
-isAltTime( const tr_session * s )
-{
-    int minutes, day;
-    tr_bool withinTime;
-    struct tm tm;
-    const time_t now = time( NULL );
-    const int begin = s->altSpeedTimeBegin;
-    const int end = s->altSpeedTimeEnd;
-    const tr_bool toNextDay = begin > end;
-
-    tr_localtime_r( &now, &tm );
-    minutes = tm.tm_hour*60 + tm.tm_min;
-    day = tm.tm_wday;
-
-    if( !toNextDay )
-        withinTime = ( begin <= minutes ) && ( minutes < end );
-    else /* goes past midnight */
-        withinTime = ( begin <= minutes ) || ( minutes < end );
-
-    if( !withinTime )
-        return FALSE;
-
-    if( toNextDay && (minutes < end) )
-    {
-        --day;
-        if( day == -1 )
-            day = 6;
-    }
-
-    return ((1<<day) & s->altSpeedTimeDay) != 0;
-}
-
-/***
-****
-***/
-
 #ifdef TR_EMBEDDED
  #define TR_DEFAULT_ENCRYPTION   TR_CLEAR_PREFERRED
 #else
@@ -503,8 +466,6 @@ onSaveTimer( int foo UNUSED, short bar UNUSED, void * vsession )
 ***/
 
 static void tr_sessionInitImpl( void * );
-static void onAltTimer( int, short, void* );
-static void setAltTimer( tr_session * session );
 
 struct init_data
 {
@@ -559,8 +520,7 @@ tr_sessionInit( const char  * tag,
     return session;
 }
 
-static void useAltSpeed( tr_session * session, tr_bool enabled, tr_bool byUser );
-static void useAltSpeedTime( tr_session * session, tr_bool enabled, tr_bool byUser );
+static void turtleCheckClock( tr_session * session, struct tr_turtle_info * t, tr_bool byUser );
 
 static void
 onNowTimer( int foo UNUSED, short bar UNUSED, void * vsession )
@@ -580,10 +540,11 @@ onNowTimer( int foo UNUSED, short bar UNUSED, void * vsession )
     if( usec > max ) usec = max;
     if( usec < min ) usec = min;
     tr_timerAdd( session->nowTimer, 0, usec );
+    /* fprintf( stderr, "time %zu sec, %zu microsec\n", (size_t)tr_time(), (size_t)tv.tv_usec ); */
 
-    /* update libtransmission's epoch time */
+    /* tr_session things to do once per second */
     tr_timeUpdate( tv.tv_sec );
-    /* fprintf( stderr, "time %zu sec, %zu microsec\n", (size_t)tr_time(), (size_t)tv.tv_usec ); */
+    turtleCheckClock( session, &session->turtle, FALSE );
 }
 
 static void loadBlocklists( tr_session * session );
@@ -636,10 +597,6 @@ tr_sessionInitImpl( void * vdata )
 
     assert( tr_isSession( session ) );
 
-    session->altTimer = tr_new0( struct event, 1 );
-    evtimer_set( session->altTimer, onAltTimer, session );
-    setAltTimer( session );
-
     session->saveTimer = tr_new0( struct event, 1 );
     evtimer_set( session->saveTimer, onSaveTimer, session );
     tr_timerAdd( session->saveTimer, SAVE_INTERVAL_SECS, 0 );
@@ -666,6 +623,8 @@ tr_sessionInitImpl( void * vdata )
     data->done = TRUE;
 }
 
+static void turtleBootstrap( tr_session *, struct tr_turtle_info *, tr_bool isEnabled );
+
 static void
 sessionSetImpl( void * vdata )
 {
@@ -677,6 +636,7 @@ sessionSetImpl( void * vdata )
     struct init_data * data = vdata;
     tr_session * session = data->session;
     tr_benc * settings = data->clientSettings;
+    struct tr_turtle_info * turtle = &session->turtle;
 
     assert( tr_isSession( session ) );
     assert( tr_bencIsDict( settings ) );
@@ -799,25 +759,26 @@ sessionSetImpl( void * vdata )
         tr_sessionSetRatioLimited( session, boolVal );
 
     /**
-    ***  Alternate speed limits
+    ***  Turtle Mode
     **/
 
+    /* update the turtle mode's fields */
     if( tr_bencDictFindInt( settings, TR_PREFS_KEY_ALT_SPEED_UP, &i ) )
-        tr_sessionSetAltSpeed( session, TR_UP, i );
+        turtle->speedLimit[TR_UP] = i;
     if( tr_bencDictFindInt( settings, TR_PREFS_KEY_ALT_SPEED_DOWN, &i ) )
-        tr_sessionSetAltSpeed( session, TR_DOWN, i );
+        turtle->speedLimit[TR_DOWN] = i;
     if( tr_bencDictFindInt( settings, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, &i ) )
-        tr_sessionSetAltSpeedBegin( session, i );
+        turtle->beginMinute = i;
     if( tr_bencDictFindInt( settings, TR_PREFS_KEY_ALT_SPEED_TIME_END, &i ) )
-        tr_sessionSetAltSpeedEnd( session, i );
+        turtle->endMinute = i;
     if( tr_bencDictFindInt( settings, TR_PREFS_KEY_ALT_SPEED_TIME_DAY, &i ) )
-        tr_sessionSetAltSpeedDay( session, i );
+        turtle->days = i;
     if( tr_bencDictFindBool( settings, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, &boolVal ) )
-        useAltSpeedTime( session, boolVal, FALSE );
-    if( boolVal )
-        useAltSpeed( session, isAltTime( session ), FALSE );
-    else if( tr_bencDictFindBool( settings, TR_PREFS_KEY_ALT_SPEED_ENABLED, &boolVal ) )
-        useAltSpeed( session, boolVal, FALSE );
+        turtle->isClockEnabled = boolVal;
+
+    if( !tr_bencDictFindBool( settings, TR_PREFS_KEY_ALT_SPEED_ENABLED, &boolVal ) )
+        boolVal = FALSE;
+    turtleBootstrap( session, turtle, boolVal );
 
     data->done = TRUE;
 }
@@ -1074,7 +1035,9 @@ tr_sessionGetRatioLimit( const tr_session * session )
 }
 
 /***
+****
 ****  SPEED LIMITS
+****
 ***/
 
 tr_bool
@@ -1107,74 +1070,128 @@ updateBandwidth( tr_session * session, tr_direction dir )
     tr_bandwidthSetDesiredSpeed( session->bandwidth, dir, limit );
 }
 
+static void
+turtleFindNextChange( struct tr_turtle_info * t )
+{
+    struct tm tm;
+    time_t today_began_at;
+    time_t next_begin;
+    time_t next_end;
+    const time_t now = tr_time( );
+    const int SECONDS_PER_DAY = 86400;
+
+    tr_localtime_r( &now, &tm );
+    tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
+    today_began_at = mktime( &tm );
+
+    next_begin = today_began_at + ( t->beginMinute * 60 );
+    if( next_begin <= now )
+        next_begin += SECONDS_PER_DAY;
+
+    next_end = today_began_at + ( t->endMinute * 60 );
+    if( next_end <= now )
+        next_end += SECONDS_PER_DAY;
+
+    if( next_begin < next_end ) {
+        t->_nextChangeAt = next_begin;
+        t->_nextChangeValue = TRUE;
+    } else {
+        t->_nextChangeAt = next_end;
+        t->_nextChangeValue = FALSE;
+    }
+
+    /* if the next change is today, look for today in t->days.
+       if the next change is tomorrow to turn limits OFF, look for today in t->days.
+       if the next change is tomorrow to turn limits ON, look for tomorrow in t->days. */
+    if( t->_nextChangeValue && (( t->_nextChangeAt >= today_began_at + SECONDS_PER_DAY )))
+        t->_nextChangeAllowed = ( t->days & ( ( tm.tm_wday + 1 ) % 7 ) ) != 0;
+    else
+        t->_nextChangeAllowed = ( t->days & tm.tm_wday ) != 0;
+
+    if( t->isClockEnabled && t->_nextChangeAllowed ) {
+        char buf[128];
+        tr_localtime_r( &t->_nextChangeAt, &tm );
+        strftime( buf, sizeof( buf ), "%a %b %d %T %Y", &tm );
+        tr_dbg( "Turtle clock updated: at %s we'll turn limits %s", buf, (t->_nextChangeValue?"on":"off") );
+    }
+}
+
 static void
 altSpeedToggled( void * vsession )
 {
     tr_session * session = vsession;
+    struct tr_turtle_info * t = &session->turtle;
 
     assert( tr_isSession( session ) );
 
     updateBandwidth( session, TR_UP );
     updateBandwidth( session, TR_DOWN );
+    turtleFindNextChange( t );
 
-    if( session->altCallback != NULL )
-        (*session->altCallback)( session, session->altSpeedEnabled, session->altSpeedChangedByUser, session->altCallbackUserData );
+    if( t->callback != NULL )
+        (*t->callback)( session, t->isEnabled, t->changedByUser, t->callbackUserData );
 }
 
-/* tell the alt speed limit timer to fire again at the top of the minute */
 static void
-setAltTimer( tr_session * session )
+useAltSpeed( tr_session * s, struct tr_turtle_info * t, tr_bool enabled, tr_bool byUser )
 {
-    const time_t now = time( NULL );
-    struct tm tm;
+    assert( tr_isSession( s ) );
+    assert( t != NULL );
+    assert( tr_isBool( enabled ) );
+    assert( tr_isBool( byUser ) );
 
-    assert( tr_isSession( session ) );
-    assert( session->altTimer != NULL );
+    if( t->isEnabled != enabled )
+    {
+        t->isEnabled = enabled;
+        t->changedByUser = byUser;
+        tr_runInEventThread( s, altSpeedToggled, s );
+    }
+}
 
-    tr_localtime_r( &now, &tm );
-    tr_timerAdd( session->altTimer, 60-tm.tm_sec, 0 );
+static tr_bool
+turtleTestClock( struct tr_turtle_info * t, tr_bool * enabled )
+{
+    tr_bool hit;
+
+    if(( hit = ( t->testedAt < t->_nextChangeAt ) && ( t->_nextChangeAt <= tr_time( ))))
+        *enabled = t->_nextChangeValue;
+
+    return hit;
 }
 
-/* this is called once a minute to:
- * (1) update session->isAltTime
- * (2) alter the speed limits when the alt limits go on and off */
 static void
-onAltTimer( int foo UNUSED, short bar UNUSED, void * vsession )
+turtleCheckClock( tr_session * session, struct tr_turtle_info * t, tr_bool byUser )
 {
-    tr_session * session = vsession;
+    tr_bool enabled;
+    const time_t now = tr_time( );
+    const tr_bool hit = turtleTestClock( t, &enabled );
 
-    assert( tr_isSession( session ) );
+    t->testedAt = now;
 
-    if( session->altSpeedTimeEnabled )
+    if( hit )
     {
-        const time_t now = time( NULL );
-        struct tm tm;
-        int currentMinute, day;
-        tr_bool isBeginTime, isEndTime, isDay;
-        tr_localtime_r( &now, &tm );
-        currentMinute = tm.tm_hour*60 + tm.tm_min;
-        day = tm.tm_wday;
-
-        isBeginTime = currentMinute == session->altSpeedTimeBegin;
-        isEndTime = currentMinute == session->altSpeedTimeEnd;
-        if( isBeginTime || isEndTime )
+        if( t->isClockEnabled && t->_nextChangeAllowed )
         {
-            /* if looking at the end date, look at the next day if end time is before begin time */
-            if( isEndTime && !isBeginTime && session->altSpeedTimeEnd < session->altSpeedTimeBegin )
-            {
-                --day;
-                if( day == -1 )
-                    day = 6;
-            }
-
-            isDay = ((1<<day) & session->altSpeedTimeDay) != 0;
-
-            if( isDay )
-                useAltSpeed( session, isBeginTime, FALSE );
+            tr_inf( "Time to turn %s turtle mode!", (enabled?"on":"off") );
+            useAltSpeed( session, t, enabled, byUser );
         }
+
+        turtleFindNextChange( t );
     }
+}
 
-    setAltTimer( session );
+/* Called after the turtle's fields are loaded from an outside source.
+ * It initializes the implementation fields 
+ * and turns on turtle mode if the clock settings say to. */
+static void
+turtleBootstrap( tr_session * session, struct tr_turtle_info * turtle, tr_bool isEnabled )
+{
+    turtleFindNextChange( turtle );
+
+    if( !isEnabled )
+        turtleTestClock( turtle, &isEnabled );
+
+    useAltSpeed( session, turtle, isEnabled, FALSE );
 }
 
 /***
@@ -1234,7 +1251,7 @@ tr_sessionSetAltSpeed( tr_session * s, tr_direction d, int KB_s )
     assert( tr_isDirection( d ) );
     assert( KB_s >= 0 );
 
-    s->altSpeed[d] = KB_s;
+    s->turtle.speedLimit[d] = KB_s;
 
     updateBandwidth( s, d );
 }
@@ -1245,30 +1262,33 @@ tr_sessionGetAltSpeed( const tr_session * s, tr_direction d )
     assert( tr_isSession( s ) );
     assert( tr_isDirection( d ) );
 
-    return s->altSpeed[d];
+    return s->turtle.speedLimit[d];
 }
 
-void
-useAltSpeedTime( tr_session * session, tr_bool enabled, tr_bool byUser )
+static void
+userPokedTheClock( tr_session * s, struct tr_turtle_info * t )
 {
-    assert( tr_isSession( session ) );
-    assert( tr_isBool( enabled ) );
-    assert( tr_isBool( byUser ) );
+    tr_dbg( "Refreshing the turtle mode clock due to user changes" );
 
-    if( session->altSpeedTimeEnabled != enabled )
-    {
-        const tr_bool isAlt = isAltTime( session );
-
-        session->altSpeedTimeEnabled = enabled;
+    t->testedAt = 0;
+    turtleFindNextChange( t );
 
-        if( enabled && session->altSpeedEnabled != isAlt )
-            useAltSpeed( session, isAlt, byUser );
-    }
+    if( t->isClockEnabled && t->_nextChangeAllowed && !t->_nextChangeValue )
+        useAltSpeed( s, t, TRUE, TRUE );
 }
+
 void
 tr_sessionUseAltSpeedTime( tr_session * s, tr_bool b )
 {
-    useAltSpeedTime( s, b, TRUE );
+    struct tr_turtle_info * t = &s->turtle;
+
+    assert( tr_isSession( s ) );
+    assert( tr_isBool ( b ) );
+
+    if( t->isClockEnabled != b ) {
+        t->isClockEnabled = b;
+        userPokedTheClock( s, t );
+    }
 }
 
 tr_bool
@@ -1276,21 +1296,18 @@ tr_sessionUsesAltSpeedTime( const tr_session * s )
 {
     assert( tr_isSession( s ) );
 
-    return s->altSpeedTimeEnabled;
+    return s->turtle.isClockEnabled;
 }
 
 void
-tr_sessionSetAltSpeedBegin( tr_session * s, int minutes )
+tr_sessionSetAltSpeedBegin( tr_session * s, int minute )
 {
     assert( tr_isSession( s ) );
-    assert( 0<=minutes && minutes<(60*24) );
-
-    if( s->altSpeedTimeBegin != minutes )
-    {
-        s->altSpeedTimeBegin = minutes;
+    assert( 0<=minute && minute<(60*24) );
 
-        if( tr_sessionUsesAltSpeedTime( s ) )
-            useAltSpeed( s, isAltTime( s ), TRUE );
+    if( s->turtle.beginMinute != minute ) {
+        s->turtle.beginMinute = minute;
+        userPokedTheClock( s, &s->turtle );
     }
 }
 
@@ -1299,21 +1316,18 @@ tr_sessionGetAltSpeedBegin( const tr_session * s )
 {
     assert( tr_isSession( s ) );
 
-    return s->altSpeedTimeBegin;
+    return s->turtle.beginMinute;
 }
 
 void
-tr_sessionSetAltSpeedEnd( tr_session * s, int minutes )
+tr_sessionSetAltSpeedEnd( tr_session * s, int minute )
 {
     assert( tr_isSession( s ) );
-    assert( 0<=minutes && minutes<(60*24) );
-
-    if( s->altSpeedTimeEnd != minutes )
-    {
-        s->altSpeedTimeEnd = minutes;
+    assert( 0<=minute && minute<(60*24) );
 
-        if( tr_sessionUsesAltSpeedTime( s ) )
-            useAltSpeed( s, isAltTime( s ), TRUE );
+    if( s->turtle.endMinute != minute ) {
+        s->turtle.endMinute = minute;
+        userPokedTheClock( s, &s->turtle );
     }
 }
 
@@ -1322,20 +1336,17 @@ tr_sessionGetAltSpeedEnd( const tr_session * s )
 {
     assert( tr_isSession( s ) );
 
-    return s->altSpeedTimeEnd;
+    return s->turtle.endMinute;
 }
 
 void
-tr_sessionSetAltSpeedDay( tr_session * s, tr_sched_day day )
+tr_sessionSetAltSpeedDay( tr_session * s, tr_sched_day days )
 {
     assert( tr_isSession( s ) );
 
-    if( s->altSpeedTimeDay != day )
-    {
-        s->altSpeedTimeDay = day;
-
-        if( tr_sessionUsesAltSpeedTime( s ) )
-            useAltSpeed( s, isAltTime( s ), TRUE );
+    if( s->turtle.days != days ) {
+        s->turtle.days = days;
+        userPokedTheClock( s, &s->turtle );
     }
 }
 
@@ -1344,28 +1355,13 @@ tr_sessionGetAltSpeedDay( const tr_session * s )
 {
     assert( tr_isSession( s ) );
 
-    return s->altSpeedTimeDay;
+    return s->turtle.days;
 }
 
-void
-useAltSpeed( tr_session * s, tr_bool enabled, tr_bool byUser )
-{
-    assert( tr_isSession( s ) );
-    assert( tr_isBool( enabled ) );
-    assert( tr_isBool( byUser ) );
-
-    if( s->altSpeedEnabled != enabled)
-    {
-        s->altSpeedEnabled = enabled;
-        s->altSpeedChangedByUser = byUser;
-
-        tr_runInEventThread( s, altSpeedToggled, s );
-    }
-}
 void
 tr_sessionUseAltSpeed( tr_session * session, tr_bool enabled )
 {
-    useAltSpeed( session, enabled, TRUE );
+    useAltSpeed( session, &session->turtle, enabled, TRUE );
 }
 
 tr_bool
@@ -1373,7 +1369,7 @@ tr_sessionUsesAltSpeed( const tr_session * s )
 {
     assert( tr_isSession( s ) );
 
-    return s->altSpeedEnabled;
+    return s->turtle.isEnabled;
 }
 
 void
@@ -1383,8 +1379,8 @@ tr_sessionSetAltSpeedFunc( tr_session       * session,
 {
     assert( tr_isSession( session ) );
 
-    session->altCallback = func;
-    session->altCallbackUserData = userData;
+    session->turtle.callback = func;
+    session->turtle.callbackUserData = userData;
 }
 
 void
@@ -1490,10 +1486,6 @@ sessionCloseImpl( void * vsession )
     tr_free( session->nowTimer );
     session->nowTimer = NULL;
 
-    evtimer_del( session->altTimer );
-    tr_free( session->altTimer );
-    session->altTimer = NULL;
-
     tr_verifyClose( session );
     tr_sharedClose( session );
     tr_rpcClose( &session->rpcServer );
index 3e5c4254d106f1c5134363b48c8144fb8cb15c91..f3ff1c57f3ef814363b494ffc37fd0f246b11461 100644 (file)
@@ -41,6 +41,62 @@ struct tr_bandwidth;
 struct tr_bindsockets;
 struct tr_fdInfo;
 
+/**
+ * How clock mode works:
+ *
+ * ._nextChangeAt, ._nextChangeValue and ._nextChangeAllowed are private fields
+ * that are derived from .days, .beginMinute, .endMinute and the current time.
+ * They're rebuilt when either (a) the user changes the clock settings or
+ * (b) when the time at ._nextChangeAt is reached.
+ *
+ * When ._nextChangeAt is reached, if .isClockEnabled and ._nextChangeAllowed
+ * are both true, then turtle mode's flag is set to ._nextChangeValue.
+ */
+struct tr_turtle_info
+{
+    /* TR_UP and TR_DOWN speed limits */
+    int speedLimit[2];
+
+    /* is turtle mode on right now? */
+    tr_bool isEnabled;
+
+    /* does turtle mode turn itself on and off at given times? */
+    tr_bool isClockEnabled;
+
+    /* when clock mode is on, minutes after midnight to turn on turtle mode */
+    int beginMinute;
+
+    /* when clock mode is on, minutes after midnight to turn off turtle mode */
+    int endMinute;
+
+    /* only use clock mode on these days of the week */
+    tr_sched_day days;
+
+    /* called when isEnabled changes */
+    tr_altSpeedFunc * callback;
+
+    /* the callback's user_data argument */
+    void * callbackUserData;
+
+    /* the callback's changedByUser argument.
+     * indicates whether the change came from the user or from the clock. */
+    tr_bool changedByUser;
+
+    /* this is the next time the clock will set turtle mode */
+    time_t _nextChangeAt;
+
+    /* the clock will set turtle mode to this flag. */
+    tr_bool _nextChangeValue;
+
+    /* When clock mode is on, only toggle turtle mode if this is true.
+     * This flag is used to filter out changes that fall on days when
+     * clock mode is disabled. */
+    tr_bool _nextChangeAllowed;
+
+    /* The last time the clock tested to see if _nextChangeAt was reached */
+    time_t testedAt;
+};
+
 /** @brief handle to an active libtransmission session */
 struct tr_session
 {
@@ -63,17 +119,7 @@ struct tr_session
     int                          speedLimit[2];
     tr_bool                      speedLimitEnabled[2];
 
-    int                          altSpeed[2];
-    tr_bool                      altSpeedEnabled;
-
-    int                          altSpeedTimeBegin;
-    int                          altSpeedTimeEnd;
-    tr_sched_day                 altSpeedTimeDay;
-    tr_bool                      altSpeedTimeEnabled;
-    tr_bool                      altSpeedChangedByUser;
-
-    tr_altSpeedFunc            * altCallback;
-    void                       * altCallbackUserData;
+    struct tr_turtle_info        turtle;
 
     struct tr_fdInfo           * fdInfo;
 
@@ -129,7 +175,6 @@ struct tr_session
 
     tr_benc                    * metainfoLookup;
 
-    struct event               * altTimer;
     struct event               * nowTimer;
     struct event               * saveTimer;