| /* |
| Copyright (C) 2006, 2005, 2004 Erik Eliasson, Johan Bilien, Werner Dittmann |
| |
| This library is free software; you can redistribute it and/or |
| modify it under the terms of the GNU Lesser General Public |
| License as published by the Free Software Foundation; either |
| version 2.1 of the License, or (at your option) any later version. |
| |
| This library 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 |
| Lesser General Public License for more details. |
| |
| You should have received a copy of the GNU Lesser General Public |
| License along with this library; if not, write to the Free Software |
| Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| |
| |
| #ifndef TIMEOUTPROVIDER_H |
| #define TIMEOUTPROVIDER_H |
| |
| /** |
| * Provides a way to request timeouts after a number of milli seconds. |
| * |
| * A command is associated to each timeout. |
| * |
| * Modified to use the common c++ library functions and the STL |
| * list by Werner Dittmann. |
| * |
| * @author Erik Eliasson, eliasson@it.kth.se, 2003 |
| * @author Werner Dittmann <Werner.Dittmann@t-online.de> |
| */ |
| |
| #include <list> |
| #include <sys/time.h> |
| |
| #include <commoncpp/config.h> |
| #include <commoncpp/thread.h> |
| |
| /** |
| * Represents a request of a "timeout" (delivery of a command to a |
| * "timeout receiver" after at least a specified time period). |
| * |
| * Slightly modified to use gettimeofday directly. |
| * |
| * NOTE: This class is only used internaly. |
| * @author Erik Eliasson |
| * @author Werner Dittmann |
| */ |
| template <class TOCommand, class TOSubscriber> |
| class TPRequest |
| { |
| |
| public: |
| |
| TPRequest( TOSubscriber tsi, int timeoutMs, const TOCommand &command): |
| subscriber(tsi) |
| { |
| struct timeval tv; |
| gettimeofday(&tv, NULL ); |
| |
| when_ms = ((uint64)tv.tv_sec) * (uint64)1000 + ((uint64)tv.tv_usec) / (uint64)1000; |
| when_ms += timeoutMs; |
| this->command = command; |
| } |
| |
| /** |
| * @param t ms since Epoch |
| */ |
| bool happensBefore(uint64 t) |
| { |
| if (when_ms < t) { |
| return true; |
| } |
| if (when_ms > t) { |
| return false; |
| } |
| return false; // if equal it does not "happens_before" |
| |
| } |
| |
| bool happensBefore(const TPRequest *req){ |
| return happensBefore(req->when_ms); |
| } |
| |
| /** |
| * Number of milli seconds until timeout from when this method is |
| * called |
| */ |
| int getMsToTimeout () |
| { |
| struct timeval tv; |
| gettimeofday(&tv, NULL ); |
| |
| uint64 now = ((uint64)tv.tv_sec) * (uint64)1000 + ((uint64)tv.tv_usec) / (uint64)1000; |
| |
| if (happensBefore(now)) { |
| return 0; |
| } |
| else { |
| return (int)(when_ms - now); |
| } |
| } |
| |
| TOCommand getCommand() |
| { |
| return command; |
| } |
| |
| TOSubscriber getSubscriber() |
| { |
| return subscriber; |
| } |
| |
| /** |
| * Two timeout requests are considered equeal if they have |
| * the same subscriber AND command AND time when they |
| * occur. If one of the time is zero then this is a |
| * wildcard and matches always. |
| */ |
| bool operator==(const TPRequest<TOCommand, TOSubscriber> &req) |
| { |
| if (req.subscriber == subscriber && |
| req.command == command && |
| req.when_ms == when_ms) { |
| return true; |
| } |
| return false; |
| } |
| |
| private: |
| TOSubscriber subscriber; |
| uint64 when_ms; // Time since Epoch in ms when the timeout |
| // will happen |
| |
| TOCommand command; // Command that will be delivered to the |
| // receiver (subscriber) of the timeout. |
| }; |
| |
| /** |
| * Class to generate objects giving timeout functionality. |
| * |
| * @author Erik Eliasson |
| * @author Werner Dittmann |
| */ |
| template<class TOCommand, class TOSubscriber> |
| class TimeoutProvider : public ost::Thread, ost::Event { |
| |
| public: |
| |
| /** |
| * Timeout Provide Constructor |
| */ |
| TimeoutProvider(): requests(), synchLock(), stop(false) { } |
| |
| /** |
| * Destructor also terminates the Timeout thread. |
| */ |
| ~TimeoutProvider() { |
| terminate(); |
| } |
| |
| /** |
| * Terminates the Timeout provider thread. |
| */ |
| void stopThread(){ |
| stop = true; |
| signal(); // signal event to waiting thread |
| } |
| |
| /** |
| * Request a timeout trigger. |
| * |
| * @param time_ms Number of milli-seconds until the timeout is |
| * wanted. Note that a small additional period of time is |
| * added that depends on execution speed. |
| * @param subscriber The receiver of the callback when the command has timed |
| * out. This argument must not be NULL. |
| * @param command Specifies the String command to be passed back in the |
| * callback. |
| */ |
| void requestTimeout(int32_t time_ms, TOSubscriber subscriber, const TOCommand &command) |
| { |
| TPRequest<TOCommand, TOSubscriber>* request = |
| new TPRequest<TOCommand, TOSubscriber>(subscriber, time_ms, command); |
| |
| synchLock.enter(); |
| |
| if (requests.size()==0) { |
| requests.push_front(request); |
| signal(); |
| synchLock.leave(); |
| return; |
| } |
| if (request->happensBefore(requests.front())) { |
| requests.push_front(request); |
| signal(); |
| synchLock.leave(); |
| return; |
| } |
| if (requests.back()->happensBefore(request)){ |
| requests.push_back(request); |
| signal(); |
| synchLock.leave(); |
| return; |
| } |
| |
| typename std::list<TPRequest<TOCommand, TOSubscriber>* >::iterator i; |
| for(i = requests.begin(); i != requests.end(); i++ ) { |
| if( request->happensBefore(*i)) { |
| requests.insert(i, request); |
| break; |
| } |
| } |
| signal(); |
| synchLock.leave(); |
| } |
| |
| /** |
| * Removes timeout requests that belong to a subscriber and command. |
| * |
| * @see requestTimeout |
| */ |
| void cancelRequest(TOSubscriber subscriber, const TOCommand &command) |
| { |
| synchLock.enter(); |
| typename std::list<TPRequest<TOCommand, TOSubscriber>* >::iterator i; |
| for(i = requests.begin(); i != requests.end(); ) { |
| if( (*i)->getCommand() == command && |
| (*i)->getSubscriber() == subscriber) { |
| i = requests.erase(i); |
| continue; |
| } |
| i++; |
| } |
| synchLock.leave(); |
| } |
| |
| protected: |
| |
| void run() |
| { |
| do { |
| synchLock.enter(); |
| int32_t time = 3600000; |
| int32_t size = 0; |
| if ((size = requests.size()) > 0) { |
| time = requests.front()->getMsToTimeout(); |
| } |
| if (time == 0 && size > 0) { |
| if (stop){ // This must be checked so that we will |
| // stop even if we have timeouts to deliver. |
| synchLock.leave(); |
| return; |
| } |
| TPRequest<TOCommand, TOSubscriber>* req = requests.front(); |
| TOSubscriber subs = req->getSubscriber(); |
| TOCommand command = req->getCommand(); |
| |
| requests.pop_front(); |
| |
| synchLock.leave(); // call the command with free Mutex |
| subs->handleTimeout(command); |
| continue; |
| } |
| synchLock.leave(); |
| if (stop) { // If we were told to stop while delivering |
| // a timeout we will exit here |
| return; |
| } |
| reset(); // ready to receive triggers again |
| wait(time); |
| if (stop) { // If we are told to exit while waiting we |
| // will exit |
| return; |
| } |
| } while(true); |
| } |
| |
| private: |
| |
| // The timeouts are ordered in the order of which they |
| // will expire. Nearest in future is first in list. |
| std::list<TPRequest<TOCommand, TOSubscriber> *> requests; |
| |
| ost::Mutex synchLock; // Protects the internal data structures |
| |
| bool stop; // Flag to tell the worker thread |
| // to terminate. Set to true and |
| // wake the worker thread to |
| // terminate it. |
| }; |
| |
| #endif |