Status CategoryBlueprintApproved (do not edit if already approved in Launchpad)
Launchpad Entry simulator-singleton
Modules affected None
Responsible MarcSchinnenburg

Summary

Have one singleton that aggregates all other "Singletons" to improve testability.

Release Note

This change will cause all users to change the way they access the EventScheduler, RandomNumberGenerator and so on ... Thus it breaks the current API. However, it is easy to add backward compatibility.

Rationale

The following problem shall be solved by this redesign:

There is an indispensable need to access one global instance of a certain type of variable within a simulator. Well known examples are the EventScheduler and the RandomNumberGenerator. Others, like a data base to store measured values can be easily thought of. Typically the Singleton pattern is applied to this problem (see "Gamma, Design Patterns" for more information). This approach has considerable drawbacks related to testing. For each test a fresh and clean environment (consisting of wns::events::scheduler::Scheduler, wns::RandomNumberGenerator, ...) is needed. With the current approach (one Singleton for each of these variables) we need to reset each global variable (which is error prone, if we forget one). If we would have one Singleton (called wns::Simulator) we would be able to reset this Singleton (wns::Singelton::getInstance().reset()), effectively resetting all "Singletons" inside this Singleton (which is much less error prone ...).

Use Cases

In each simulation. In each test.

Requirements

Especially the last point is hard to solve with standard singletons, since these typically don't have parameters at all.

Design

The idea is to have only ONE singleton which aggregates all other Singletons in itself. This singleton needs to be constructed at the very beginning of the simulation (say after int main() {...), to ensure all other Singletons are available.

Inlined image: openWNS_SimulatorInterface.png
Figure: UML diagram of the Simulator interface and its associated singeltons

The according interface in C++:

   1 class ISimulator
   2 {
   3 public:
   4     virtual events::scheduler::Scheduler*
   5     getEventScheduler() = 0;
   6 
   7     virtual RandomNumberGenerator*
   8     getRandomNumberGenerator() = 0;
   9 
  10     virtual DataBase*
  11     getDataBase() = 0;
  12 
  13     virtual logger::Master*
  14     getMasterLogger() = 0;
  15 
  16     virtual pyconfig::View
  17     getConfiguration() = 0;
  18 };

Additionally, we will have a SingletonHolder which provides the access to the Singleton:

   1 class SimulatorSingletonHolder
   2 {
   3 public:
   4     // No instance of ISimulator set at startup
   5     SimulatorSingletonHolder();
   6 
   7     // Throw if no instance avaiable
   8     ISimulator*
   9     getInstance();
  10 
  11     // Inject instance, throw if instance already set
  12     void
  13     setInstance(ISimulator*);
  14 };
  15 
  16 static SimulatorSingeltonHolder GlobalSimulator;

Implementation

An implementation might look as follows:

   1 // The normal Simulator, you would use in a simulation
   2 class Simulator :
   3     public ISimulator
   4 {
   5 public:
   6     Simulator(const pyconfig::View& config) :
   7         masterConfig(config)
   8     {
   9         this->setupEventScheduler(config.get('eventScheduler'));
  10         this->setupRandomNumberGenerator(config.get('randomNumberGenerator'));
  11         this->setupMasterLogger(config.get('masterLogger'));
  12         this->setupDataBase(config.get('dataBase'));
  13     }
  14 
  15     pyconfig::View
  16     getConfiguration()
  17     {
  18         return masterConfig;
  19     }
  20 
  21     // ... implementation of other getters here
  22 
  23 private:
  24     void
  25     setupEventHandler(const pyconfig::View& config);
  26 
  27     // ... and so on
  28 };

A simulation core can then roughly look as follows:

Inlined image: openWNS_SimulatorStartup.png
Figure: UML Sequence Diagram of simulation startup

or in C++:

   1 int main(int argc, char* argv[])
   2 {
   3     // get config from somewhere ...
   4 
   5     Simulator sim(config);
   6     GlobalSimulator::setInstance(&sim);
   7 
   8     // may be we need even more here:
   9     sim.startup();
  10     sim.run();
  11     sim.shutdown();
  12 }

Different implementation (with and without networking, GUI or other features) become easily possible now and can be cleanly separated.

UI Changes

   1 EventScheduler* es = EventScheduler::getInstance();
   2 // now reads
   3 EventScheduler* es = wns::GlobalSimulator::getInstance()->getEventScheduler();

Code Changes

Code changes should include an overview of what needs to change, and in some cases even the specific details.

Migration

Include:

Test/Demo Plan

It's important that we are able to test new features, and demonstrate them to users. Use this section to describe a short plan that anybody can follow that demonstrates the feature is working. This can then be used during CD testing, and to show off after release.

This need not be added or completed until the specification is nearing beta.

Open Issues

This should highlight any issues that should be addressed in further specifications, and not problems with the specification itself; since any specification with problems cannot be approved.

BoF agenda and discussion

Use this section to take notes during the BoF; if you keep it in the approved spec, use it for summarising what was discussed and note any options that were rejected.


openWNS: Development/Blueprints/simulator-singleton (last edited 2008-08-21 10:50:49 by localhost)