S3FC project page S3FC home page

Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members   Related Pages  

s3_periodic_notifier.cc

Go to the documentation of this file.
00001 /*
00002  * Stone Three Foundation Class (s3fc) provides a number of utility classes.
00003  * Copyright (C) 2001 by Stone Three Signal Processing (Pty) Ltd.
00004  *
00005  * Authored by Stone Three Signal Processing (Pty) Ltd.
00006  *
00007  * This library is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public
00009  * License as published by the Free Software Foundation; either
00010  * version 2.1 of the License, or (at your option) any later version.
00011  * 
00012  * This library is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Lesser General Public License for more details.
00016  * 
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with this library; if not, write to the Free Software
00019  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00020  * 
00021  * Please see the file 'COPYING' in the source root directory.
00022  */
00023 
00033 #include <s3fc/s3_periodic_notifier.h>
00034 #include <s3fc/s3_exception.h>
00035 
00036 #include <sstream>
00037 #include <algorithm>
00038 // WIN32 compatibility [rfanner]
00039 #ifdef _WIN32
00040 // Define NOMINMAX to prevent the definition of min and max macros in windef.h
00041 #define NOMINMAX
00042 #include <wtypes.h>
00043 #include <winbase.h>
00044 #endif
00045 
00046 s3_periodic_notifier::event_loop* s3_periodic_notifier::ev_loop = 0;
00047 s3_mutex s3_periodic_notifier::ev_loop_lock;
00048 
00049 //
00050 s3_periodic_notifier::event_loop::event_loop()
00051 {
00052    // start myself
00053    start();
00054 }
00055 
00056 //
00057 s3_periodic_notifier::event_loop::~event_loop()
00058 {
00059    request_terminate();
00060    wait_on_exit();
00061    // deletion can now safely continue....
00062 }
00063 
00064 //
00065 void s3_periodic_notifier::event_loop::add_client(s3_periodic_notifier* n_id)
00066 {
00067    state_lock.lock();
00068    // test precondition - violation is internal error
00069    if ( is_client(n_id) )
00070    {
00071       state_lock.unlock();
00072       // client already here
00073       std::stringstream str;
00074       str << "Client [" << n_id << "] already in list";
00075       throw s3_generic_exception("s3_periodic_notifier::event_loop::"
00076              "add_client()", str.str());
00077    }
00078    // add to list with zero period
00079    client_list[n_id] = 0;
00080    state_lock.unlock();
00081 }
00082 
00083 //
00084 void s3_periodic_notifier::event_loop::remove_client(s3_periodic_notifier* n_id)
00085 {
00086    state_lock.lock();
00087    // test precondition - violation is internal error
00088    if ( ! is_client(n_id) )
00089    {
00090       state_lock.unlock();
00091       // client already here
00092       std::stringstream str;
00093       str << "Client [" << n_id << "] not in list";
00094       throw s3_generic_exception("s3_periodic_notifier::event_loop::"
00095              "remove_client()", str.str());
00096    }
00097    // remove from list
00098    client_list.erase(n_id);
00099    state_lock.unlock();
00100 }
00101 
00102 //
00103 void s3_periodic_notifier::event_loop::set_period(s3_periodic_notifier* n_id,
00104                     const period_t& n_period)
00105 {
00106    state_lock.lock();
00107    // test precondition - violation is internal error
00108    if ( ! is_client(n_id) )
00109    {
00110       state_lock.unlock();
00111       // client already here
00112       std::stringstream str;
00113       str << "Client [" << n_id << "] not in list";
00114       throw s3_generic_exception("s3_periodic_notifier::event_loop::"
00115              "set_period()", str.str());
00116    }
00117    // set the period
00118    client_list[n_id] = n_period;
00119    state_lock.unlock();
00120 }
00121 
00122 //
00123 bool s3_periodic_notifier::event_loop::clients() const
00124 {
00125    bool c;
00126    state_lock.lock();
00127    c = ( client_list.size() > 0 ) ;
00128    state_lock.unlock();
00129    return c;
00130 }
00131 
00132 
00133 //
00134 bool s3_periodic_notifier::event_loop::is_client(s3_periodic_notifier* n_id)
00135 {
00136    return (client_list.count(n_id) != 0);
00137 }
00138 
00139 //
00140 void s3_periodic_notifier::event_loop::main_loop()
00141 {
00142    // BIG NOTE: The slot_size_ms number may NEVER exceed 1000, i.e. a
00143    // maximum slot size of 1 second is enforced. This is due to the way
00144    // in which the timing calculations is performed in here. This again
00145    // is due to the non-linear representation of POSIX time (sec,nsec pair).
00146    // If this needs to be changed the part that calculates delta_ms would 
00147    // require some attention.
00148 
00149    // size of a timing interval (in milliseconds as we humans like ms)
00150    const int interval_size_ms = 100;
00151    // also in nanoseconds (as POSIX prefers ns)
00152    const int interval_size_ns = 1000000 * interval_size_ms;
00153 
00154    // interval counter - count the number of slot_size_ms intervals
00155    // each interval is aligned to the absolute system time base, as
00156    // reported by clock_gettime(CLOCK_REALTIME, ...).
00157    unsigned long interval_cnt = 0;
00158    // time until the start of the next interval timeslot,
00159    // aligned to system time
00160    long delta_ns;
00161 #ifdef _WIN32
00162    // For Windows implementation [rfanner].
00163    // As the POSIX real-time functions clock_gettime and nanosleep
00164    // are not readily available in Windows, we use the GetTickCount
00165    // and Sleep functions supplied by Windows. Alternately, if the 
00166    // time precision turns out to be too low, one could turn to the
00167    // high precision time functions QueryPerformanceCounter and 
00168    // QueryPerformanceFrequency.
00169 
00170    // The output and input of GetTickCount and Sleep are of the type
00171    // DWORD, which is a 32-bit unsigned integer.
00172    // the time in milliseconds to sleep
00173    DWORD msecs_to_sleep = 0;
00174 #else
00175    // For Linux implementation [rfanner].
00176    // need this for clock_gettime and nanosleep. these are POSIX 1003.1c-1995
00177    timespec ts;
00178 #endif
00179 
00180    while(true)
00181    {
00182       if (test_terminate()) break;
00183 #ifdef _WIN32
00184       // For Windows implementation [rfanner].
00185       // Determine when the next time interval must commence
00186       DWORD current_time = GetTickCount();
00187       // align with clock, and find the remaining milliseconds until the
00188       // next interval
00189       // Note that it does not matter if wrap-around of GetTickCount has 
00190       // occured, as the modulus provides a relative measure.
00191       msecs_to_sleep = interval_size_ms - (current_time % interval_size_ms);
00192       // sleep for the specified interval.
00193       Sleep(msecs_to_sleep);      
00194 #else
00195       // get the time according to the RTC
00196       clock_gettime(CLOCK_REALTIME, &ts);
00197       // align our intervals with the RTC - calculate the remaining number
00198       // of nanoseconds until the start of the next interval
00199       delta_ns = interval_size_ns - (ts.tv_nsec % ( interval_size_ns ));
00200 
00201       // sleep for delta_ns
00202       ts.tv_sec = 0;
00203       ts.tv_nsec = delta_ns;
00204       nanosleep(&ts, 0);
00205 #endif
00206       // increment interval counter
00207       interval_cnt++;
00208       
00209       // notify
00210       state_lock.lock();
00211       for (list_t::iterator ptr = client_list.begin();
00212       ptr != client_list.end();
00213       ptr++) 
00214       {
00215     // this client period
00216     period_t cp_ms = ptr->second;
00217     if ( cp_ms > 0 ) // zero period ignored
00218     {
00219        if ((cp_ms/interval_size_ms) == 0) // period too small - 
00220           // use minimum and verify every interval
00221        {
00222           ptr->first->notify();
00223        }
00224        // modulo safe as we checked for zero denominator
00225        else  if ((interval_cnt % (cp_ms/interval_size_ms)) == 0)
00226        {
00227           ptr->first->notify();
00228        }
00229     }
00230       }
00231       state_lock.unlock();
00232    }
00233 }
00234 
00235 //
00236 s3_periodic_notifier::s3_periodic_notifier(period_t n_period_ms) :
00237    period_ms(n_period_ms),
00238    enabled(false)
00239 {
00240    ev_loop_lock.lock();
00241    if ( ev_loop == 0 )
00242    {
00243       ev_loop = new event_loop();
00244    }
00245    ev_loop->add_client(this);
00246    ev_loop->set_period(this, n_period_ms);
00247    ev_loop_lock.unlock();
00248 }
00249 
00250 
00251 //
00252 s3_periodic_notifier::~s3_periodic_notifier()
00253 {
00254    ev_loop_lock.lock();
00255    ev_loop->remove_client(this);
00256    // if i am the last one to leave take ev_loop with me
00257    if  (! ev_loop->clients())
00258    {
00259       delete ev_loop;
00260       ev_loop = 0;
00261    }
00262    ev_loop_lock.unlock();
00263 }
00264 
00265 //
00266 void s3_periodic_notifier::enable()
00267 {
00268    state_lock.lock();
00269    enabled = true;
00270    state_lock.unlock();
00271 }
00272 
00273 //
00274 void s3_periodic_notifier::disable()
00275 {
00276    state_lock.lock();
00277    enabled = false;
00278    state_lock.unlock();
00279 }
00280 
00281 //
00282 void s3_periodic_notifier::set_period(period_t n_period_ms)
00283 {
00284    state_lock.lock();
00285    period_ms = n_period_ms;
00286    ev_loop->set_period(this, n_period_ms);
00287    state_lock.unlock();
00288 }
00289 
00290 //
00291 void s3_periodic_notifier::subscribe(s3_semaphore& sem)
00292 {
00293    state_lock.lock();
00294 
00295    // check precondition (sem already subscribed) and throw if violation
00296    if ( is_subscribed(sem) )
00297    {
00298       state_lock.unlock();
00299       // alreay in list
00300       std::stringstream str;
00301       str << "Semaphore at [" << &sem << "] already subscribed";
00302       throw s3_generic_exception("s3_periodic_notifier::subscribe()",
00303              str.str());
00304    }
00305    notification_list.push_back(&sem);
00306    state_lock.unlock();
00307 }
00308 
00309 //
00310 void s3_periodic_notifier::unsubscribe(s3_semaphore& sem)
00311 {
00312    state_lock.lock();
00313 
00314    // check precondition (sem already subscribed) and throw if violation
00315    if ( ! is_subscribed(sem) )
00316    {
00317       state_lock.unlock();
00318       // not in list
00319       std::stringstream str;
00320       str << "Semaphore at [" << &sem << "] not subscribed";
00321       throw s3_generic_exception("s3_periodic_notifier::unsubscribe()",
00322              str.str());
00323    }
00324    notification_list.erase(std::find(notification_list.begin(), 
00325                  notification_list.end(), 
00326                  &sem));
00327    state_lock.unlock();
00328 }
00329 
00330 //
00331 void s3_periodic_notifier::notify()
00332 {
00333    state_lock.lock();
00334    // do nothing if we're not currently enabled
00335    if ( enabled )
00336    {
00337       for (std::list<s3_semaphore*>::iterator ptr = notification_list.begin();
00338       ptr != notification_list.end();
00339       ptr++)
00340       {
00341     (*ptr)->post();
00342       }
00343    }
00344    state_lock.unlock();
00345 }
00346 
00347 //
00348 bool s3_periodic_notifier::is_subscribed(s3_semaphore& sem){
00349    return ( std::find(notification_list.begin(), 
00350             notification_list.end(), 
00351             &sem) != notification_list.end() );
00352 }
00353 

Send comments to: s3fc@stonethree.com SourceForge Logo