![]() |
Peano
|
#include <Service.h>
Public Member Functions | |
virtual | ~Service () |
virtual void | receiveDanglingMessages ()=0 |
Receive the dangling messages from the MPI subsystem. | |
Protected Member Functions | |
virtual void | shutdown ()=0 |
Protected Attributes | |
tarch::multicore::RecursiveSemaphore | _receiveDanglingMessagesSemaphore |
Recursive semaphores. | |
Friends | |
class | ServiceRepository |
Service Interface.
A service is a singleton running the background which is available always. If it is shut down, all calls to the service becomes nop, i.e. it is a robust thing. On a parallel machine, services are also do exist only once, i.e. they are global singletons. Therefore, you shall never access a service directly, but always via a proxy/accessor. This proxy either invokes the service directly (if the services are on the local node) or it creates an MPI call to the real service. Consequently, services have to poll the MPI queue regularly. To enable them to do so, all services register at the service repository, and this service repository has a poll operation. Services also have to be protected by semaphores in a multicore environment.
If you wanna write a service yourself, you have to do two things:
I usually make services singletons. I can then do the registration in the constructor.
Historically, I preferred the make singletons classes with private constructor that offer a getInstance() routine. The latter is implemented as follows:
MyClass MyClass::getInstance() { static MyClass singleton; return singleton; }
But this pattern breaks down with Peano's static libraries. At least one some systems, I've observed that singletons still are created multiple times, i.e. not only when we first call getInstance(). The canonical solution to this dilemma is to make the singleton a static (class) attribute which is private and to make getInstance() hand out this static attribute.
While this solves the problem of multiple instantiation, it does not solve another problem: If the constructor of a singleton registers the singleton, it could happen that the linker creates the singletons in the wrong order. There's no way to ensure that the tarch's service is created first. So what can happen is that the memory for the ServiceRepository (where all the services register) is allocated. Then, an arbitrary service is created and registers, i.e. it adds is pointer to the repository. Later, the repository's constructor is invoked and effectively overwrites the registered service. It is hence important that we create the repository first. The only way to ensure this, is to init all services manually.
So here's the takeaway:
(1) Singletons are always realised via a private constructor. (2) Singletons are always realised via a class attribute. (3) Singletons don't do anything in their constructor. They are not allowed to. (4) Any singleton has an init() operation, as well as a shutdown (if required).
To enforce this behaviour with init() and shutdown(), I make shutdown an abstract functions. You therefore have to implement it. I refrained from doing the same with init(), as services might want init() to have service-specific arguments.
|
pure virtual |
Receive the dangling messages from the MPI subsystem.
This, sometimes, is an absolutely necessary operation to ensure that some MPI queues are freed again and MPI can continue to process. If you have too full buffers - in particular full of unexpected messages - you run into a situation where no eager or unexpected receiving can be done at all.
If you invoke this routine, it is absolutely essential that you always do it via the services. The reasons are discussed in ServiceRepository::receiveDanglingMessages().
Implemented in exahype2::RefinementControlService, peano4::parallel::SpacetreeSet, tarch::mpi::BooleanSemaphore::BooleanSemaphoreService, and tarch::services::ServiceRepository.
|
protectedpure virtual |
|
friend |
|
protected |
Recursive semaphores.
We have ensure that receiveDanglingMessages is not ran in parallel with other operations on one rank. This holds for example for MPI updates or updates of lists of MPIRequests. Yet, there can be cases where receiveDanglingMessages() in turn invokes receiveDanglingMessages() recursively. With a standard semaphore, we would now run into a deadlock as default semaphores are not recursion-safe. Therefore, I provide this recursive semaphore. The ServiceRepository does already lock it, so receiveDanglingMessages() invoked through the factory should be fine. You might however want to lock it yourself, too.
I had severe issues when I outsourced the tarch into a library. The static initialisation of the recursive semaphores then led to seg faults from time to time. Therefore, I decided to make this semaphore a pointer. It is initialised lazily tarch::services::ServiceRepository::receiveDanglingMessages().