summaryrefslogtreecommitdiff
path: root/cpp
diff options
context:
space:
mode:
Diffstat (limited to 'cpp')
-rwxr-xr-xcpp/allTests.py1
-rw-r--r--cpp/include/IceUtil/IceUtil.h1
-rw-r--r--cpp/include/IceUtil/Timer.h116
-rw-r--r--cpp/src/IceUtil/Makefile1
-rw-r--r--cpp/src/IceUtil/Makefile.mak1
-rw-r--r--cpp/src/IceUtil/Timer.cpp195
-rw-r--r--cpp/test/IceUtil/Makefile3
-rw-r--r--cpp/test/IceUtil/Makefile.mak3
-rw-r--r--cpp/test/IceUtil/timer/.depend1
-rw-r--r--cpp/test/IceUtil/timer/Client.cpp163
-rw-r--r--cpp/test/IceUtil/timer/Makefile29
-rw-r--r--cpp/test/IceUtil/timer/Makefile.mak33
-rwxr-xr-xcpp/test/IceUtil/timer/run.py39
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)