diff options
author | Benoit Foucher <benoit@zeroc.com> | 2007-08-28 14:29:40 +0200 |
---|---|---|
committer | Benoit Foucher <benoit@zeroc.com> | 2007-08-28 14:29:40 +0200 |
commit | f4367dedecfef432263c9d9c29d25da9b1e31a13 (patch) | |
tree | 126125174908dfb1f33893ba3acf45426d2c351c | |
parent | Fixed compilation on hp (diff) | |
download | ice-f4367dedecfef432263c9d9c29d25da9b1e31a13.tar.bz2 ice-f4367dedecfef432263c9d9c29d25da9b1e31a13.tar.xz ice-f4367dedecfef432263c9d9c29d25da9b1e31a13.zip |
Added new IceUtil::Timer class
-rwxr-xr-x | cpp/allTests.py | 1 | ||||
-rw-r--r-- | cpp/include/IceUtil/IceUtil.h | 1 | ||||
-rw-r--r-- | cpp/include/IceUtil/Timer.h | 116 | ||||
-rw-r--r-- | cpp/src/IceUtil/Makefile | 1 | ||||
-rw-r--r-- | cpp/src/IceUtil/Makefile.mak | 1 | ||||
-rw-r--r-- | cpp/src/IceUtil/Timer.cpp | 195 | ||||
-rw-r--r-- | cpp/test/IceUtil/Makefile | 3 | ||||
-rw-r--r-- | cpp/test/IceUtil/Makefile.mak | 3 | ||||
-rw-r--r-- | cpp/test/IceUtil/timer/.depend | 1 | ||||
-rw-r--r-- | cpp/test/IceUtil/timer/Client.cpp | 163 | ||||
-rw-r--r-- | cpp/test/IceUtil/timer/Makefile | 29 | ||||
-rw-r--r-- | cpp/test/IceUtil/timer/Makefile.mak | 33 | ||||
-rwxr-xr-x | cpp/test/IceUtil/timer/run.py | 39 |
13 files changed, 584 insertions, 2 deletions
diff --git a/cpp/allTests.py b/cpp/allTests.py index 1394e3039af..ac3800b38b3 100755 --- a/cpp/allTests.py +++ b/cpp/allTests.py @@ -71,6 +71,7 @@ tests = [ \ "IceUtil/unicode", \ "IceUtil/inputUtil", \ "IceUtil/uuid", \ + "IceUtil/timer", \ "Slice/errorDetection", \ "Ice/proxy", \ "Ice/operations", \ diff --git a/cpp/include/IceUtil/IceUtil.h b/cpp/include/IceUtil/IceUtil.h index bda71873593..74e3994c29d 100644 --- a/cpp/include/IceUtil/IceUtil.h +++ b/cpp/include/IceUtil/IceUtil.h @@ -47,5 +47,6 @@ #include <IceUtil/Time.h> #include <IceUtil/UUID.h> #include <IceUtil/Unicode.h> +#include <IceUtil/Timer.h> #endif diff --git a/cpp/include/IceUtil/Timer.h b/cpp/include/IceUtil/Timer.h new file mode 100644 index 00000000000..da5cb38054f --- /dev/null +++ b/cpp/include/IceUtil/Timer.h @@ -0,0 +1,116 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2007 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#ifndef ICEUTIL_TIMER_H +#define ICEUTIL_TIMER_H + +#include <IceUtil/Shared.h> +#include <IceUtil/Thread.h> +#include <IceUtil/Monitor.h> +#include <IceUtil/Time.h> + +#include <set> +#include <map> + +namespace IceUtil +{ + +class Timer; +typedef IceUtil::Handle<Timer> TimerPtr; + +// +// Extend the TimerTask class and override the run() method to execute +// code at a specific time or repeatedly. +// +class TimerTask : public IceUtil::Shared +{ +public: + + virtual ~TimerTask() { } + + virtual bool operator<(const TimerTask& r) const; + + virtual void run() = 0; +}; +typedef IceUtil::Handle<TimerTask> TimerTaskPtr; + +// +// The timer class is used to schedule tasks for one-time execution or +// repeated execution. Tasks are executed by the dedicated timer thread +// sequentially. +// +class Timer : public virtual IceUtil::Shared, private virtual IceUtil::Thread +{ +public: + + // + // Construct a timer and starts its execution thread. + // + Timer(); + + // + // Destroy the timer and join with its execution thread. + // + void destroy(); + + // + // Schedule a task for execution at a given time. + // + void schedule(const TimerTaskPtr&, const IceUtil::Time&); + + // + // Schedule a task for repeated execution with the given delay + // between each execution. + // + void scheduleRepeated(const TimerTaskPtr&, const IceUtil::Time&); + + // + // Cancel a task. Returns true if the task has not yet run or if + // it's a task scheduled for repeated execution. Returns false if + // the task has already run, was already cancelled or was never + // schedulded. + // + bool cancel(const TimerTaskPtr&); + +private: + + struct Token + { + IceUtil::Time scheduledTime; + IceUtil::Time delay; + TimerTaskPtr task; + + bool operator<(const Token& r) const + { + if(scheduledTime < r.scheduledTime) + { + return true; + } + else if(scheduledTime > r.scheduledTime) + { + return false; + } + + return task.get() < r.task.get(); + } + }; + + virtual void run(); + + IceUtil::Monitor<IceUtil::Mutex> _monitor; + bool _destroyed; + std::set<Token> _tokens; + std::map<TimerTaskPtr, IceUtil::Time> _tasks; +}; +typedef IceUtil::Handle<Timer> TimerPtr; + +} + +#endif + diff --git a/cpp/src/IceUtil/Makefile b/cpp/src/IceUtil/Makefile index 219e9ef1948..efe8560f7e0 100644 --- a/cpp/src/IceUtil/Makefile +++ b/cpp/src/IceUtil/Makefile @@ -36,6 +36,7 @@ OBJS = ArgVector.o \ Thread.o \ ThreadException.o \ Time.o \ + Timer.o \ UUID.o \ Unicode.o diff --git a/cpp/src/IceUtil/Makefile.mak b/cpp/src/IceUtil/Makefile.mak index 87f17898cf4..fe8f1cbb7ba 100644 --- a/cpp/src/IceUtil/Makefile.mak +++ b/cpp/src/IceUtil/Makefile.mak @@ -35,6 +35,7 @@ OBJS = ArgVector.obj \ Thread.obj \ ThreadException.obj \ Time.obj \ + Timer.obj \ UUID.obj \ Unicode.obj diff --git a/cpp/src/IceUtil/Timer.cpp b/cpp/src/IceUtil/Timer.cpp new file mode 100644 index 00000000000..d44534a82cd --- /dev/null +++ b/cpp/src/IceUtil/Timer.cpp @@ -0,0 +1,195 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2007 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#include <IceUtil/Timer.h> +#include <IceUtil/Exception.h> + +using namespace std; +using namespace IceUtil; + +bool +TimerTask::operator<(const TimerTask& r) const +{ + return this < &r; +} + +Timer::Timer() : _destroyed(false) +{ + start(); +} + +void +Timer::destroy() +{ + { + IceUtil::Monitor<IceUtil::Mutex>::Lock sync(_monitor); + if(_destroyed) + { + return; + } + _destroyed = true; + _monitor.notify(); + _tokens.clear(); + } + getThreadControl().join(); +} + +void +Timer::schedule(const TimerTaskPtr& task, const IceUtil::Time& time) +{ + IceUtil::Monitor<IceUtil::Mutex>::Lock sync(_monitor); + if(_destroyed) + { + return; + } + +#if defined(_MSC_VER) && (_MSC_VER < 1300) + Token token; + token.scheduledTime = time; + token.task = task; +#else + const Token token = { time, IceUtil::Time(), task }; +#endif + _tokens.insert(token); + _tasks.insert(make_pair(task, token.scheduledTime)); + + if(token.scheduledTime <= _tokens.begin()->scheduledTime) + { + _monitor.notify(); + } +} + +void +Timer::scheduleRepeated(const TimerTaskPtr& task, const IceUtil::Time& delay) +{ + IceUtil::Monitor<IceUtil::Mutex>::Lock sync(_monitor); + if(_destroyed) + { + return; + } + +#if defined(_MSC_VER) && (_MSC_VER < 1300) + Token token; + token.scheduledTime = IceUtil::Time::now() + delay; + token.delay = delay; + token.task = task; +#else + Token token = { IceUtil::Time::now() + delay, delay, task }; +#endif + _tokens.insert(token); + _tasks.insert(make_pair(task, token.scheduledTime)); + + if(token.scheduledTime <= _tokens.begin()->scheduledTime) + { + _monitor.notify(); + } +} + +bool +Timer::cancel(const TimerTaskPtr& task) +{ + IceUtil::Monitor<IceUtil::Mutex>::Lock sync(_monitor); + map<TimerTaskPtr, IceUtil::Time>::iterator p = _tasks.find(task); + if(p == _tasks.end()) + { + return false; + } + _tasks.erase(p); + +#if defined(_MSC_VER) && (_MSC_VER < 1300) + Token token; + token.scheduledTime = p->second; + token.task = task = p->first; +#else + Token token = { p->second, IceUtil::Time(), p->first }; +#endif + _tokens.erase(token); + return true; +} + +void +Timer::run() +{ + Token token; + while(true) + { + { + IceUtil::Monitor<IceUtil::Mutex>::Lock sync(_monitor); + + if(!_destroyed) + { + // + // If the task we just ran is a repeated task, schedule it + // again for executation if it wasn't canceled. + // + if(token.delay != IceUtil::Time()) + { + map<TimerTaskPtr, IceUtil::Time>::iterator p = _tasks.find(token.task); + if(p != _tasks.end()) + { + token.scheduledTime = IceUtil::Time::now() + token.delay; + p->second = token.scheduledTime; + _tokens.insert(token); + } + } + token = Token(); + + if(_tokens.empty()) + { + _monitor.wait(); + } + } + + if(_destroyed) + { + break; + } + + while(!_tokens.empty() && !_destroyed) + { + const IceUtil::Time now = IceUtil::Time::now(); + const Token& first = *(_tokens.begin()); + if(first.scheduledTime <= now) + { + _tokens.erase(_tokens.begin()); + if(token.delay == IceUtil::Time()) + { + _tasks.erase(token.task); + } + token = first; + break; + } + + _monitor.timedWait(first.scheduledTime - now); + } + + if(_destroyed) + { + break; + } + } + + if(token.task) + { + try + { + token.task->run(); + } + catch(const std::exception& e) + { + cerr << "IceUtil::Timer::run(): uncaught exception: "; + cerr << e.what() << endl; + } + catch(...) + { + cerr << "IceUtil::Timer::run(): uncaught exception" << endl; + } + } + } +} diff --git a/cpp/test/IceUtil/Makefile b/cpp/test/IceUtil/Makefile index 0f36cbf0aa5..0df25e078d1 100644 --- a/cpp/test/IceUtil/Makefile +++ b/cpp/test/IceUtil/Makefile @@ -16,7 +16,8 @@ SUBDIRS = condvar \ unicode \ inputUtil \ uuid \ - ctrlCHandler + ctrlCHandler \ + timer $(EVERYTHING):: @for subdir in $(SUBDIRS); \ diff --git a/cpp/test/IceUtil/Makefile.mak b/cpp/test/IceUtil/Makefile.mak index a144895cd03..d90fcbe69cf 100644 --- a/cpp/test/IceUtil/Makefile.mak +++ b/cpp/test/IceUtil/Makefile.mak @@ -16,7 +16,8 @@ SUBDIRS = condvar \ unicode \ inputUtil \ uuid \ - ctrlCHandler + ctrlCHandler \ + timer $(EVERYTHING):: @for %i in ( $(SUBDIRS) ) do \ diff --git a/cpp/test/IceUtil/timer/.depend b/cpp/test/IceUtil/timer/.depend new file mode 100644 index 00000000000..099366289c4 --- /dev/null +++ b/cpp/test/IceUtil/timer/.depend @@ -0,0 +1 @@ +Client$(OBJEXT): Client.cpp ../../../include/IceUtil/Timer.h ../../../include/IceUtil/Shared.h ../../../include/IceUtil/Config.h ../../../include/IceUtil/Mutex.h ../../../include/IceUtil/Lock.h ../../../include/IceUtil/ThreadException.h ../../../include/IceUtil/Exception.h ../../../include/IceUtil/Thread.h ../../../include/IceUtil/Handle.h ../../../include/IceUtil/Monitor.h ../../../include/IceUtil/Cond.h ../../../include/IceUtil/Time.h ../../include/TestCommon.h diff --git a/cpp/test/IceUtil/timer/Client.cpp b/cpp/test/IceUtil/timer/Client.cpp new file mode 100644 index 00000000000..013e33277e1 --- /dev/null +++ b/cpp/test/IceUtil/timer/Client.cpp @@ -0,0 +1,163 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2007 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#include <IceUtil/Timer.h> +#include <TestCommon.h> + +#include <vector> + +using namespace IceUtil; +using namespace std; + +class TestTask : public IceUtil::TimerTask, IceUtil::Monitor<IceUtil::Mutex> +{ +public: + + TestTask() + { + } + + TestTask(const IceUtil::Time& scheduledTime) : _scheduledTime(scheduledTime) + { + } + + virtual void + run() + { + Lock sync(*this); + ++_count; + _run = IceUtil::Time::now(); + //cerr << "run: " << _scheduledTime.toMicroSeconds() << " " << _run.toMicroSeconds() << endl; + notifyAll(); + } + + virtual bool + operator<(const TestTask& r) const + { + return _scheduledTime < r._scheduledTime; + } + + virtual bool + hasRun() const + { + Lock sync(*this); + return _run != IceUtil::Time(); + } + + int + getCount() const + { + Lock sync(*this); + return _count; + } + + virtual IceUtil::Time + getRunTime() const + { + Lock sync(*this); + return _run; + } + + IceUtil::Time + getScheduledTime() const + { + return _scheduledTime; + } + + virtual void + waitForRun() + { + Lock sync(*this); + while(_run == IceUtil::Time()) + { + if(!timedWait(IceUtil::Time::seconds(10))) + { + test(false); // Timeout. + } + } + } + +private: + + IceUtil::Time _run; + IceUtil::Time _scheduledTime; + int _count; +}; +typedef IceUtil::Handle<TestTask> TestTaskPtr; + +int main(int argc, char* argv[]) +{ + cout << "testing timer... " << flush; + { + IceUtil::TimerPtr timer = new IceUtil::Timer(); + + { + TestTaskPtr task = new TestTask(); + timer->schedule(task, IceUtil::Time::now()); + task->waitForRun(); + } + + { + TestTaskPtr task = new TestTask(); + test(!timer->cancel(task)); + timer->schedule(task, IceUtil::Time::now() + IceUtil::Time::seconds(1)); + test(!task->hasRun() && timer->cancel(task) && !task->hasRun()); + test(!timer->cancel(task)); + IceUtil::ThreadControl::sleep(IceUtil::Time::milliSeconds(1100)); + test(!task->hasRun()); + } + + { + vector<TestTaskPtr> tasks; + IceUtil::Time start = IceUtil::Time::now() + IceUtil::Time::milliSeconds(100); + for(int i = 0; i < 100; ++i) + { + tasks.push_back(new TestTask(start + IceUtil::Time::milliSeconds(i))); + } + + random_shuffle(tasks.begin(), tasks.end()); + + for(vector<TestTaskPtr>::const_iterator p = tasks.begin(); p != tasks.end(); ++p) + { + timer->schedule(*p, (*p)->getScheduledTime()); + } + + for(vector<TestTaskPtr>::const_iterator p = tasks.begin(); p != tasks.end(); ++p) + { + (*p)->waitForRun(); + } + + test(IceUtil::Time::now() - start > IceUtil::Time::milliSeconds(99)); + + sort(tasks.begin(), tasks.end()); + for(vector<TestTaskPtr>::const_iterator p = tasks.begin(); p + 1 != tasks.end(); ++p) + { + if((*p)->getRunTime() > (*(p + 1))->getRunTime()) + { + test(false); + } + } + } + + { + TestTaskPtr task = new TestTask(); + timer->scheduleRepeated(task, IceUtil::Time::milliSeconds(20)); + IceUtil::ThreadControl::sleep(IceUtil::Time::milliSeconds(500)); + test(task->hasRun() && task->getCount() > 15 && task->getCount() < 26); + test(timer->cancel(task)); + int count = task->getCount(); + IceUtil::ThreadControl::sleep(IceUtil::Time::milliSeconds(100)); + test(count == task->getCount()); + } + + timer->destroy(); + } + cout << "ok" << endl; + return EXIT_SUCCESS; +} diff --git a/cpp/test/IceUtil/timer/Makefile b/cpp/test/IceUtil/timer/Makefile new file mode 100644 index 00000000000..e24d058530a --- /dev/null +++ b/cpp/test/IceUtil/timer/Makefile @@ -0,0 +1,29 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2007 ZeroC, Inc. All rights reserved. +# +# This copy of Ice is licensed to you under the terms described in the +# ICE_LICENSE file included in this distribution. +# +# ********************************************************************** + +top_srcdir = ../../.. + +CLIENT = client + +TARGETS = $(CLIENT) + +OBJS = Client.o + + +SRCS = $(OBJS:.o=.cpp) + +include $(top_srcdir)/config/Make.rules + +CPPFLAGS := -I. -I../../include $(CPPFLAGS) + +$(CLIENT): $(OBJS) + rm -f $@ + $(CXX) $(LDFLAGS) -o $@ $(OBJS) $(BASELIBS) + +include .depend diff --git a/cpp/test/IceUtil/timer/Makefile.mak b/cpp/test/IceUtil/timer/Makefile.mak new file mode 100644 index 00000000000..eb223c31957 --- /dev/null +++ b/cpp/test/IceUtil/timer/Makefile.mak @@ -0,0 +1,33 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2007 ZeroC, Inc. All rights reserved. +# +# This copy of Ice is licensed to you under the terms described in the +# ICE_LICENSE file included in this distribution. +# +# ********************************************************************** + +top_srcdir = ..\..\.. + +CLIENT = client.exe + +TARGETS = $(CLIENT) + +OBJS = Client.obj + +SRCS = $(OBJS:.obj=.cpp) + +!include $(top_srcdir)/config/Make.rules.mak + +CPPFLAGS = -I. -I../../include $(CPPFLAGS) -DWIN32_LEAN_AND_MEAN + +!if "$(CPP_COMPILER)" != "BCC2006" && "$(OPTIMIZE)" != "yes" +PDBFLAGS = /pdb:$(CLIENT:.exe=.pdb) +!endif + +$(CLIENT): $(OBJS) + $(LINK) $(LD_EXEFLAGS) $(PDBFLAGS) $(SETARGV) $(OBJS) $(PREOUT)$@ $(PRELIBS)$(BASELIBS) + @if exist $@.manifest echo ^ ^ ^ Embedding manifest using $(MT) && \ + $(MT) -nologo -manifest $@.manifest -outputresource:$@;#1 && del /q $@.manifest + +!include .depend diff --git a/cpp/test/IceUtil/timer/run.py b/cpp/test/IceUtil/timer/run.py new file mode 100755 index 00000000000..d7cb0424185 --- /dev/null +++ b/cpp/test/IceUtil/timer/run.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python +# ********************************************************************** +# +# Copyright (c) 2003-2007 ZeroC, Inc. All rights reserved. +# +# This copy of Ice is licensed to you under the terms described in the +# ICE_LICENSE file included in this distribution. +# +# ********************************************************************** + +import os, sys + +for toplevel in [".", "..", "../..", "../../..", "../../../.."]: + toplevel = os.path.normpath(toplevel) + if os.path.exists(os.path.join(toplevel, "config", "TestUtil.py")): + break +else: + raise "can't find toplevel directory!" + +sys.path.append(os.path.join(toplevel, "config")) +import TestUtil + +name = os.path.join("IceUtil", "timer") +testdir = os.path.join(toplevel, "test", name) + +client = os.path.join(testdir, "client") + +print "starting client...", +clientPipe = os.popen(client + " 2>&1") +print "ok" + +TestUtil.printOutputFromPipe(clientPipe); + +clientStatus = TestUtil.closePipe(clientPipe) + +if clientStatus: + sys.exit(1) + +sys.exit(0) |