Project

General

Profile

Guide to writing and using services

Overview.

A service in the context of art is a class for which an instance is configured at runtime by a FHiCL configuration and made available for use by modules and other services. It must be declared to the art framework as a service using one of several declaration macros.

In addition to (optionally) having a header by means of which a module or other service may call functions, a service also has the opportunity to register functions with art to be called at appropriate moments such as immediately prior to processing an event, or after the modules' endRun functions have been executed.

What can a service do?

A service generally interacts with resources or information outside the purview of the modules. Example would be TFileService, which manages a secondary ROOT-format file for histograms, or services which provide an interface to geometry or calibration information.

What should a service not do?

In general, a service should not provide "backdoor" communication between modules of physics data or anything that ought to be stored in an event, subrun or run and have its provenance tracked.

Services implementing an interface.

A service may either be a, "standard" service, or implement a service interface. A service interface defines a base class which is registered as a service interface. A service then inherits from that interface and declares itself to the art framework as implementing same. Subsequently, the art system can be configured at runtime via FHiCL to use one or another service that implements a particular interface. External users of that interface (other services, or on occasion the art framework itself) access the service via the interface without ever knowing eactly which service implements the service it needs.

Using a service.

Configuring a service for use.

Art-provided services.

If the service to be used is an art-provided service such as Timing or SimpleMemoryCheck, then it should be configured for use as follows:

services.SimpleMemoryCheck: { }

Non-art-provided services.

services.user.MyService: { }

Services implementing an interface.

In services or services.user, as appropriate:

InterfaceName: { service_provider: ServiceName }

For example:

services.user.CatalogServiceInterface: { service_provider: IFCatalogInterface }

Accessing a service.

Services should be accessed via an instantiation of the art::ServiceHandle template e.g. to access the TFileService from your module, one would:

art::ServiceHandle<art::TFileService> h;
and then treat the handle so created as any other smart pointer.

If you intend to access a service from another service, you should obtain a ServiceHandle for the second service from the constructor of the first, even if you don't plan to use it there. This ensures the correct order of construction (and destruction) of the services, which would be alphabetical by class name otherwise.

Any library containing code which uses a service should link to that service explicitly.

Performance and caching considerations.

Obtaining an art::ServiceHandle is relatively expensive, and should not be done in tight loops; it is a reasonable course of action to cache the handle in your module class. De-referencing the handle is relatively cheap, although not zero cost, as for certain types of service a check is done to verify that the actual service referenced is appropriate for the context in which the handle is now being used. If necessary, the handle is "re-seated" for the new context.

In summary: cache the Handle at class or module entry point (analyze(), filter(), etc.) scope. Do not cache a bare pointer to a service at greater than module entry point scope otherwise problems are likely when parallelism is in operation.

Writing a service.

Service scope and registration with the framework.

A service may have one of three "scope" indicators:

  • LEGACY
    All currently defined services are "legacy" in scope, including art-defined services. You should (for now) define all your services to be LEGACY. The presence of even one LEGACY service in the configuration precludes the simultaneous processing of multiple events ("multi-schedule" operation).
  • GLOBAL
    Defining a service as "global" in scope gives a guarantee of thread saftey: the service may be called from anywhere at any time simultaneously and continue to behave correctly.
  • PER_SCHEDULE
    These services will be instantiated possibly multiple times if the art framework is configured for multi-schedule operation.

ServiceMacros.h

#include "art/Framework/Services/Registry/ServiceMacros.h"

In general, DECLARE macro invocations should be in the header file, if it exists. DEFINE macro invocations should be in the implementation.

Declare a "standard" service.

DECLARE_ART_SERVICE(myexpt::MyService,LEGACY)

Define a "standard" service.

DEFINE_ART_SERVICE(myexpt::MyService)

Declare an interface.

DECLARE_ART_SERVICE_INTERFACE(myexpt::Interface,LEGACY)

Declare a service implementing an interface.

DECLARE_ART_SERVICE_INTERFACE_IMPL(myexpt::MyService,myexpt::Interface,LEGACY)

Define a service implementing an interface.

DEFINE_ART_SERVICE_INTERFACE_IMPL(myexpt::MyService,myexpt::Interface)

Constructor.

MyService(fhicl::ParameterSet const &, art::ActivityRegistry &);

The service is provided at construction time with access to its configuration parameters and to the source:art/Framework/Services/Registry/ActivityRegistry.h for the purposes of registering for callbacks at any of a large number of synchronization points in the execution of the art framework.

Registering for callbacks.

Register for callbacks by invoking a watch() function on the signal data member of ActivityRegistry that corresponds to the point in the execution of the art framework that you wish your function to be called. For instance:
aReg.sPreBeginRun.watch(&MyService::preBeginRun, *this);
The function you provide must have the signature specified for that signal. The first template parameter of the signal specifies the calling order of registered callbacks; the second is the return type of the callback function; any subsequent parameters specify the ordered parameters of the callback function. The provided function may be any of:
  • A free function, bound or unbound.
  • A const or non-const member function of your service or any other class, bound or (if you provide an object with which to bind it) unbound.
  • A functor whose operator () signature matches the required signature.

Service interfaces and interface implementations.

A service may optionally implement one and only one interface. The interface definition takes the form of a header file containing a class definition and interface declaration. An interface implementation must inherit publicly from the interface, and declare and define itself as an implementation of that interface.

Compiling the service plugin.

A service plugin is compiled into a plugin recognizable to the art framework by making a library whose name is lib[dir1_dir2_]ClassName_service.so.