NCBI C++ ToolKit
request_control.cpp
Go to the documentation of this file.
00001 /*  $Id: request_control.cpp 45743 2010-05-12 17:16:18Z ivanov $
00002  * ===========================================================================
00003  *
00004  *                            PUBLIC DOMAIN NOTICE
00005  *               National Center for Biotechnology Information
00006  *
00007  *  This software/database is a "United States Government Work" under the
00008  *  terms of the United States Copyright Act.  It was written as part of
00009  *  the author's official duties as a United States Government employee and
00010  *  thus cannot be copyrighted.  This software/database is freely available
00011  *  to the public for use. The National Library of Medicine and the U.S.
00012  *  Government have not placed any restriction on its use or reproduction.
00013  *
00014  *  Although all reasonable efforts have been taken to ensure the accuracy
00015  *  and reliability of the software and data, the NLM and the U.S.
00016  *  Government do not and cannot warrant the performance or results that
00017  *  may be obtained by using this software or data. The NLM and the U.S.
00018  *  Government disclaim all warranties, express or implied, including
00019  *  warranties of performance, merchantability or fitness for any particular
00020  *  purpose.
00021  *
00022  *  Please cite the author in any work or product based on this material.
00023  *
00024  * ===========================================================================
00025  *
00026  * Authors:  Denis Vakatov, Vladimir Ivanov, Victor Joukov
00027  *
00028  * File Description:
00029  *   Test for request test control classes.
00030  *
00031  */
00032 
00033 #include <ncbi_pch.hpp>
00034 #include <corelib/ncbi_limits.h>
00035 #include <corelib/ncbi_system.hpp>
00036 #include <corelib/request_control.hpp>
00037 
00038 
00039 /** @addtogroup Utility
00040  *
00041  * @{
00042  */
00043 
00044 BEGIN_NCBI_SCOPE
00045 
00046 
00047 CRequestRateControl::CRequestRateControl(
00048         unsigned int    num_requests_allowed,
00049         CTimeSpan       per_period,
00050         CTimeSpan       min_time_between_requests,
00051         EThrottleAction throttle_action,
00052         EThrottleMode   throttle_mode)
00053 {
00054     Reset(num_requests_allowed, per_period, min_time_between_requests,
00055           throttle_action, throttle_mode);
00056 }
00057 
00058 
00059 void CRequestRateControl::Reset(
00060         unsigned int    num_requests_allowed,
00061         CTimeSpan       per_period,
00062         CTimeSpan       min_time_between_requests,
00063         EThrottleAction throttle_action,
00064         EThrottleMode   throttle_mode)
00065 {
00066     // Save parameters
00067     m_NumRequestsAllowed     = num_requests_allowed;
00068     m_PerPeriod              = per_period.GetAsDouble();
00069     m_MinTimeBetweenRequests = min_time_between_requests.GetAsDouble();
00070     if ( throttle_action == eDefault ) {
00071         m_ThrottleAction = eSleep;
00072     } else {
00073         m_ThrottleAction = throttle_action;
00074     }
00075     m_Mode = throttle_mode;
00076 
00077     // Reset internal state
00078     m_NumRequests  =  0;
00079     m_LastApproved = -1;
00080     m_TimeLine.clear();
00081     m_StopWatch.Restart();
00082 }
00083 
00084 
00085 bool CRequestRateControl::x_Approve(EThrottleAction action, CTimeSpan *sleeptime)
00086 {
00087     if ( sleeptime ) {
00088         *sleeptime = CTimeSpan(0,0);
00089     }
00090     // Is throttler disabled, that always approve request
00091     if ( m_NumRequests == kNoLimit ) {
00092         return true;
00093     }
00094     // Redefine default action
00095     if ( action == eDefault ) {
00096         action = m_ThrottleAction;
00097     }
00098 
00099     bool empty_period  = (m_PerPeriod <= 0);
00100     bool empty_between = (m_MinTimeBetweenRequests <= 0);
00101 
00102     // Check maximum number of requests at all (if times not specified)
00103     if ( !m_NumRequestsAllowed  ||  (empty_period  &&  empty_between) ) {
00104         if ( m_NumRequests >= m_NumRequestsAllowed ) {
00105             switch(action) {
00106                 case eErrCode:
00107                     return false;
00108                 case eSleep:
00109                     // cannot sleep in this case, return FALSE
00110                     if ( !sleeptime ) {
00111                         return false;
00112                     }
00113                     // or throw exception, see ApproveTime()
00114                 case eException:
00115                     NCBI_THROW(
00116                         CRequestRateControlException, eNumRequestsMax, 
00117                         "CRequestRateControl::Approve(): "
00118                         "Maximum number of requests exceeded"
00119                     );
00120                 case eDefault: ;
00121             }
00122         }
00123     }
00124 
00125     // Special case for eDiscrete mode and empty time between requests.
00126     // We don't need to get time marks in this case, just increase number
00127     // of requests and approve it.
00128     if ( m_Mode == eDiscrete  &&  !empty_period  &&  empty_between  &&
00129          m_NumRequests < m_NumRequestsAllowed
00130          ) {
00131         if (m_TimeLine.size() == 0) {
00132             // Save only first request time, used in x_CleanTimeLine()
00133             TTime now = m_StopWatch.Elapsed();
00134             m_TimeLine.push_back(now);
00135             // We will not update m_LastApproved except first time,
00136             // we don't needed this information in this case.
00137             m_LastApproved = now;
00138         }
00139         m_NumRequests++;
00140         // Approve request
00141         return true;
00142     }
00143 
00144     // Get current time
00145     TTime now = m_StopWatch.Elapsed();
00146     TTime x_sleeptime = 0;
00147 
00148     // Check number of requests per period
00149     if ( !empty_period ) {
00150         x_CleanTimeLine(now);
00151         if ( m_Mode == eContinuous ) {
00152             m_NumRequests = (unsigned int)m_TimeLine.size();
00153         }
00154         if ( m_NumRequests >= m_NumRequestsAllowed ) {
00155             switch(action) {
00156                 case eSleep:
00157                     // Get sleep time
00158                     _ASSERT(m_TimeLine.size() > 0);
00159                     x_sleeptime = m_TimeLine.front() + m_PerPeriod - now;
00160                     break;
00161                 case eErrCode:
00162                     return false;
00163                 case eException:
00164                     NCBI_THROW(
00165                         CRequestRateControlException,
00166                         eNumRequestsPerPeriod, 
00167                         "CRequestRateControl::Approve(): "
00168                         "Maximum number of requests per period exceeded"
00169                     );
00170                 case eDefault: ;
00171             }
00172         }
00173     }
00174     // Check time between two consecutive requests
00175     if ( !empty_between  &&  (m_LastApproved >= 0) ) {
00176         if ( now - m_LastApproved < m_MinTimeBetweenRequests ) {
00177             switch(action) {
00178                 case eSleep:
00179                     // Get sleep time
00180                     {{
00181                         TTime st = m_LastApproved + m_MinTimeBetweenRequests - now;
00182                         // Get max of two sleep times
00183                         if ( st > x_sleeptime ) {
00184                             x_sleeptime = st;
00185                         }
00186                     }}
00187                     break;
00188                 case eErrCode:
00189                     return false;
00190                 case eException:
00191                     NCBI_THROW(
00192                         CRequestRateControlException,
00193                         eMinTimeBetweenRequests, 
00194                         "CRequestRateControl::Approve(): The time "
00195                         "between two consecutive requests is too short"
00196                     );
00197                 case eDefault: ;
00198             }
00199         }
00200     }
00201 
00202     // eSleep case
00203     
00204     if ( x_sleeptime > 0 ) {
00205         if ( sleeptime ) {
00206             // ApproveTime() -- request is not approved,
00207             // return sleeping time.
00208             if ( sleeptime ) {
00209                 *sleeptime = CTimeSpan(x_sleeptime);
00210             }
00211             return false;
00212         } else {
00213             // Approve() -- sleep before approve
00214             Sleep(CTimeSpan(x_sleeptime));
00215             now = m_StopWatch.Elapsed();
00216         }
00217     }
00218     // Update stored information
00219     if ( !empty_period ) {
00220         m_TimeLine.push_back(now);
00221     }
00222     m_LastApproved = now;
00223     m_NumRequests++;
00224     // Approve request
00225     return true;
00226 }
00227 
00228 
00229 void CRequestRateControl::Sleep(CTimeSpan sleep_time)
00230 {
00231     if ( sleep_time <= CTimeSpan(0, 0) ) {
00232         return;
00233     }
00234     long sec = sleep_time.GetCompleteSeconds();
00235     // We cannot sleep that much milliseconds, round it to seconds
00236     if (sec > long(kMax_ULong / kMicroSecondsPerSecond)) {
00237         SleepSec(sec);
00238     } else {
00239         unsigned long ms;
00240         ms = sec * kMicroSecondsPerSecond +
00241              sleep_time.GetNanoSecondsAfterSecond() / 1000;
00242         if (sleep_time.GetNanoSecondsAfterSecond() % 1000) ms++;
00243         SleepMicroSec(ms);
00244     }
00245 }
00246 
00247 
00248 void CRequestRateControl::x_CleanTimeLine(TTime now)
00249 {
00250     switch (m_Mode) {
00251 
00252     case eContinuous: {
00253         // Find first non-expired item
00254         TTimeLine::iterator current;
00255         for ( current = m_TimeLine.begin(); current != m_TimeLine.end();
00256             ++current) {
00257             if ( now - *current < m_PerPeriod) {
00258                 break;
00259             }
00260         }
00261         // Erase all expired items
00262         m_TimeLine.erase(m_TimeLine.begin(), current);
00263         break;
00264     }
00265     case eDiscrete: {
00266         if (m_TimeLine.size() > 0) {
00267             if (now - m_TimeLine.front() > m_PerPeriod) {
00268                 // Period ends, remove all restrictions
00269                 m_LastApproved = -1;
00270                 m_TimeLine.clear();
00271                 m_NumRequests = 0;
00272             }
00273         }
00274         break;
00275     }
00276     } // switch
00277 }
00278 
00279 
00280 const char* CRequestRateControlException::GetErrCodeString(void) const
00281 {
00282     switch (GetErrCode()) {
00283     case eNumRequestsMax:         return "eNumRequestsMax";
00284     case eNumRequestsPerPeriod:   return "eNumRequestsPerPeriod";
00285     case eMinTimeBetweenRequests: return "eMinTimeBetweenRequests";
00286     default:                      return CException::GetErrCodeString();
00287     }
00288 }
00289 
00290 
00291 /* @} */
00292 
00293 END_NCBI_SCOPE
Modified on Wed May 23 12:56:56 2012 by modify_doxy.py rev. 337098