Quantitative Analysis
Parallel Processing
Numerical Analysis
C++ Multithreading
Python for Excel
Python Utilities
Services
Author

I. Installation.
II. Threading primitives.
III. NonBlockingQueue.
IV. ThreadPool.
V. ThreadMaster.
VI. OTS Scheduler.
1. Scheduler implementation.
2. Customization of Scheduler. Interfaces IOrigin and IProxy.
3. Acceptance test for the Scheduler.
VII. Bibliography
Downloads. Index. Contents.

Customization of Scheduler. Interfaces IOrigin and IProxy.


he Scheduler accepts directives via instances of CreateEvent, DeleteEvent and UpdateEvent classes supplied to the operator() of the Scheduler's interface. Before any action is taken, the Scheduler needs to process a CreateEvent. Consider the following C++ code.

01\ Scheduler sch;

02\ int counter=1;

03\ CreateEvent ce1("Node1",Proxy(),Origin(),counter);

04\ sch(ce1);

05\ ce1.origin().waitOn(counter,1);

Line 01: Create the Scheduler object.

Line 02: Set a synchronization counter value.

Line 03: Create a CreateEvent object. Proxy() and Origin() are default implementations of IProxy and IOrigin interfaces. These will be explained in the moment. The "Node1" is the identification string for the dependency node that is to be created.

Line 04: The Scheduler accepts the CreateEvent.

Line 05: Wait for a notification that the processing of the CreateEvent has been completed. Once the Line 05 releases control, the Scheduler is ready to receive UpdateEvent and DeleteEvent objects directed to the "Node1".

An IOrigin-implementing object is a reference to the client application that originally supplied the CreateEvent. It is a sink for all replies and diagnostic messages. An IProxy-implementing object is the reference to the business object that implements the business logic. It receives "execute now" messages. These two interfaces are the hooks that the user has to customize to adapt the Scheduler to do anything useful. We next explain these interfaces, default implementation and provide an example of IProxy and IOrigin-implementing objects that record all supplied information into a text file. The following section ( Acceptance test for the Scheduler section ) describes a working example.

The following code illustrates the IOrigin and its default implementation.

1\namespace ots { namespace scheduler {

2\

3\ class Origin

4\ {

5\ public:

6\ typedef config::SynchCounter::type SynchCounter;

7\ enum event

8\ {

9\ _firstEvent=1,

10\ created,

11\ nodeIsAbsent,

12\ failedToDelete,

13\ failedToCreate,

14\ aParentAbsent,

...

27\ _lastEvent

28\ };

29\ static std::string eventToString( event e );

30\ struct IOrigin

31\ {

32\ typedef config::SynchCounter::type SynchCounter;

33\ virtual void notify( const SynchCounter&, event) volatile =0;

34\ virtual void notify( event) volatile =0;

35\ virtual event waitOn( const SynchCounter&, int ) volatile =0;

36\ virtual std::string toString() volatile const =0;

37\ };

38\ public:

39\ class DefaultImpl : public IOrigin

40\ {

...

60\ protected:

61\ void defaultNotify( const SynchCounter&, event) volatile;

62\ void defaultNotify( event) volatile;

63\ event defaultWaitOn( const SynchCounter&, int ) volatile;

64\ };

65\ private:

66\ boost::shared_ptr<volatile IOrigin> theImpl;

67\ public:

68\ Origin() : theImpl(new DefaultImpl()) {}

69\ explicit Origin( const boost::shared_ptr<IOrigin>& impl ) : theImpl(impl) {}

70\ Origin( IOrigin* impl ) : theImpl(impl) {}

71\ Origin( const Origin& origin ) : theImpl(origin.theImpl) {}

72\ Origin& operator=( const Origin& origin ) { theImpl=origin.theImpl; return *this; }

73\ void notify( const SynchCounter& c, event e );

74\ void notify( event e );

75\ event waitOn( const SynchCounter& c, int times ) { return theImpl->waitOn(c,times); }

76\ std::string toString() const;

77\ };

The lines 30-37 are the definition of the interface IOrigin. The notify functions are called by the Scheduler when anything note-worthy happens. The enum event in the lines 7-28 describes the possibilities. The function waitOn is a synchronization tool. It blocks the current thread until it detects that the current Origin object received int-many calls to notify with the synchronization counter at least as large the one supplied as the argument. The lines 68-76 contain the implementation of the IOrigin interface and copy-by-value semantics.

The following code illustrates possible custom implementation of the IOrigin. The function makeFileOrigin, lines 38-41 below, returns an Origin object that sends all calls to notify into a test file.

1\ class FileOrigin : public Origin::DefaultImpl

2\ {

3\ public:

4\ typedef config::ConditionVariableGuard<FileOrigin>::type Guard;

5\ typedef config::ConstConditionVariableGuard<FileOrigin>::type ConstGuard;

6\ private:

7\ std::string theName;

8\ std::ofstream theFile;

9\ public:

10\ FileOrigin( const std::string& name, const std::string& fileName )

11\ :

12\ theName(name),

13\ theFile(fileName.c_str())

14\ {}

15\ virtual void notify( const SynchCounter& c, Origin::event e ) volatile

16\ {

17\ defaultNotify(c,e);

18\ {

19\ Guard guard(this,theMutex);

20\ guard->theFile<<guard->theName<<" : "<<c<<" : "<<Origin::eventToString(e)<<std::endl;

21\ }

22\ }

23\ virtual void notify( Origin::event e ) volatile

24\ {

25\ defaultNotify(e);

26\ {

27\ Guard guard(this,theMutex);

28\ guard->theFile<<guard->theName<<" : "<<Origin::eventToString(e)<<std::endl;

29\ }

30\ }

31\ virtual std::string toString() const volatile

32\ {

33\ ConstGuard guard(this,theMutex);

34\ return std::string("FileOrigin(")+guard->theName+std::string(")");

35\ }

36\ };

37\

38\ Origin makeFileOrigin( const std::string& name, const std::string& fileName )

39\ {

40\ return Origin(new FileOrigin(name,fileName));

41\ }

The following code illustrates the IProxy and default implementation.

1\namespace ots { namespace scheduler {

2\

3\ struct IProxy

4\ {

5\ virtual bool update() =0;

6\ virtual void cancelUpdate() =0;

7\ virtual void dispose() =0;

8\ virtual std::string toString() const =0;

9\ };

10\

11\ class Proxy

12\ {

13\ private:

14\ struct Null : public IProxy

15\ {

16\ virtual bool update() { return false; }

17\ virtual void cancelUpdate() {}

18\ virtual void dispose() {}

19\ virtual std::string toString() const { return std::string("Proxy::Null()"); }

20\ };

21\ boost::shared_ptr<IProxy> theImpl;

22\ public:

23\ Proxy( IProxy* impl ) : theImpl(impl) {}

24\ explicit Proxy( const boost::shared_ptr<IProxy>& impl ) : theImpl(impl) {}

25\ Proxy() : theImpl(new Null()) {}

26\ Proxy( const Proxy& proxy ) : theImpl(proxy.theImpl) {}

27\ Proxy& operator=( const Proxy& proxy ) { theImpl=proxy.theImpl; return *this; }

28\ inline bool update() { return theImpl->update(); }

29\ inline void cancelUpdate() { theImpl->cancelUpdate(); }

30\ inline void dispose() { theImpl->dispose(); }

31\ inline std::string toString() const { return theImpl->toString(); }

32\ };

33\

34\}}

The lines 3-9 are the definition of IProxy interface. The function update() is called when the Scheduler finds that it is time to update the business object. When the update() returns control the Scheduler assumes that the business object finished updating. The function cancelUpdate() is called when the Scheduler is recovering from some catastrophic situation such as boost::thread_resource_error. In such situation the Scheduler is discarding all update events and notifies both Origin and Proxy about such action. The function dispose() is called when the dependency node is removed. This is the result of a DeleteEvent processing.

The following code is an example of custom implementation of Proxy. The function makeFileProxy, lines 28-31 below, returns a Proxy object that registers all calls in a text file.

1\ class FileProxy : public IProxy

2\ {

3\ private:

4\ std::string theName;

5\ std::ofstream theFile;

6\ public:

7\ FileProxy( const std::string& name, const std::string& fileName ) : theName(name),theFile(fileName.c_str()) {}

8\ virtual ~FileProxy() {}

9\ bool update()

10\ {

11\ theFile<<theName<<" : Updated"<<std::endl;

12\ return true;

13\ }

14\ void cancelUpdate()

15\ {

16\ theFile<<theName<<" : UpdateCanceled"<<std::endl;

17\ }

18\ void dispose()

19\ {

20\ theFile<<theName<<" : Disposed"<<std::endl;

21\ }

22\ std::string toString() const

23\ {

24\ return std::string("ConsoleProxy(")+theName+std::string(")");

25\ }

26\ };

27\

28\ Proxy makeFileProxy( const std::string& name, const std::string& fileName )

29\ {

30\ return Proxy(new FileProxy(name,fileName));

31\ }

Note that the Origin's implementation has calls to synchronization primitives. The Proxy, on the other hand, is thread-oblivious.





Downloads. Index. Contents.


















Copyright 2007