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.
A. Node class.
B. Exception handling policy.
2. Customization of Scheduler. Interfaces IOrigin and IProxy.
3. Acceptance test for the Scheduler.
VII. Bibliography
Downloads. Index. Contents.

Scheduler implementation.


n the section ( Scheduler chapter ) we presented the idea of Scheduler and general requirements. The picture ( Scheduler general design ) represents the general design.





Scheduler general design
Scheduler general design

There are two copies of the dependency tree. Each tree consists of proxies that hold references to actual business objects located elsewhere (outside of Scheduler class or out-of-process or on a different machine). The Switch directs new events into the first copy. The second copy does processing of previously accumulated requests (events) and sends replies to clients and processing orders to actual business objects. When the second copy finishes the processing then the Switch reverts its direction: the first copy starts processing and the second copy accepts new events (requests).

There are three types of requests (events) that are processed in substantially different manner. The DeleteEvent is a request to remove a proxy from dependency tree. The CreateEvent is a request to insert a proxy and establish dependencies. The UpdateEvent is the signal that an existing proxy now points to an out-of-date business object. There is no logic inside of Scheduler that detects a situation when some client sends an UpdateEvent or DeleteEvent to a proxy that does not exists. The Scheduler will not crash but the Event will be lost. Hence, it is client's responsibility to wait for a reply that confirms creation or deletion of a proxy.

The Scheduler class is a light reference to a Singleton. This reflects the assumption that there is no reason to have more then one Scheduler per process. However, there may be need to have more the one Scheduler in an application if there is a class of business objects that needs quicker processing.

The following is the prototype of the Scheduler class.

1\namespace ots { namespace scheduler {

2\

3\ class CreateEvent;

4\ class DeleteEvent;

5\ class UpdateEvent;

6\

7\ class Scheduler

8\ {

9\ public:

10\ Scheduler();

11\ Scheduler( const Scheduler& sch );

12\ Scheduler& operator= (const Scheduler* sch);

13\ public:

14\ void acceptCreateEvent( const CreateEvent& event ) volatile;

15\ void acceptDeleteEvent( const DeleteEvent& event ) volatile;

16\ void acceptUpdateEvent( const UpdateEvent& event ) volatile;

17\ void run();

18\ void doNotQuit() volatile;

19\ void doQuit() volatile;

20\ };

21\

22\}} //namespace ots,scheduler

The example member function implementations are as follows.

Scheduler::Scheduler( const Scheduler& sch ) {}

Scheduler& Scheduler::operator= (const Scheduler* sch) { return *this; }

void Scheduler::run() { instance(); }

void Scheduler::acceptCreateEvent( const CreateEvent& event ) volatile { instance().acceptCreateEvent(event); }

The instance() is the Meyer singleton function that returns an Impl class defined in a local compiler scope in a cpp-file.

1\namespace ots { namespace scheduler {

2\

3\class Switch : boost::noncopyable

4\{

5\public:

6\...

7\ enum { numberOfStates=2 };

8\ unsigned char inputIndex() const volatile;

9\ unsigned char processingIndex() const volatile;

10\...

11\};

12\

13\class Impl : boost::noncopyable

14\{

15\...

16\public:

17\ typedef config::RWMutex::type QuitMutex;

18\ typedef config::ErrorLog::type ErrorLog;

19\ typedef config::ConsecutiveQueue<DeleteEvent>::type DeleteQueue;

20\ typedef config::ConsecutiveQueue<CreateEvent>::type CreateQueue;

21\ typedef config::SignaledBarrier::type SignaledBarrier;

22\ typedef config::CycleCounter::type CycleCounter;

23\ typedef NonBlockingQueue<Node::Impl> UpdateQueue;

24\ typedef NonBlockingQueue<Node::Impl> WaitingQueue;

25\private:

26\ mutable QuitMutex theQuitMutex;

27\ bool theQuit;

28\ Switch theSwitch;

29\ CycleCounter theCycleCounter;

30\ DeleteQueue theDeleteQueues[Switch::numberOfStates];

31\ CreateQueue theCreateQueues[Switch::numberOfStates];

32\ UpdateQueue theUpdateQueue;

33\ WaitingQueue theWaitingQueues[Switch::numberOfStates];

34\ NodeRegistry theRegistry[Switch::numberOfStates];

35\ SignaledBarrier theInputDataBarriers[Switch::numberOfStates];

36\...

37\};

38\

39\volatile Impl& instance()

40\{

41\ volatile static Impl impl;

42\ return impl;

43\}

Lines 6, 10, 15, 36. At this point we are concentrating on the member fields of the Impl class. We omitted the member functions and implementation details.

Lines 19,20. The ConsequtiveQueue is a simple store-by-value container with a mutex that has to be locked before every access operation. We use such object to store CreateEvents and DeleteEvents because we expect that there will be very few of those. The application would be loaded before the work day starts. Afterwards, the new Create and Delete requests result from rare manual operations.

Lines 24 and 33. The new UpdateEvent objects are stored in the WaitingQueue. The WaitingQueue type is a typedef of NonBlockingQueue. Hence, submission of a new UpdateEvent is a quick operation without memory overhead for repeated submission.

Lines 23 and 32. The UpdateQueue is the queue that contains at all times the Nodes (of the dependency tree) that are ready for immediate processing. These are, by definition, the out-of-date Nodes that do not have out-of-date parents in the dependency tree.

Line 34. The NodeRegistry is a map from InstanceNames (identifier of business object and its proxy) to Nodes. It is the container that assumes ownership of Nodes for purposes of memory management.

Line 35. theInputDataBarriers are included to prevent idle cycling if there is no new requests to process. theInputDataBarrier is closed when there is no new data and open otherwise.

Lines 26,27. theQuit field indicates whether there has been a request to exit processing. theQuitMutex is the mutex that protects theQuit.

Note that all accept* functions have only basic exception guarantee. This is so because of possible thread_resource_error. Hence, business objects should be resilient to repeated submission of the same update request.




A. Node class.
B. Exception handling policy.

Downloads. Index. Contents.


















Copyright 2007