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

All services are configured within the services block of an art-job configuration file. To enable, MyService, one would specify:

services.MyService: { }

Services implementing an interface

In services, as appropriate:

InterfaceName: { service_provider: ServiceName }

For example:

services.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. For thread-safety reasons, you are encouraged to use const-qualified template arguments when constructing a handle:
art::ServiceHandle<MyService const> h;
which will provide read-only access to the service instance.

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 two "scope" indicators:

  • LEGACY
    Most currently defined services are "legacy" in scope, including art-defined services. The presence of even one LEGACY service in the configuration precludes the simultaneous processing of multiple events ("multi-schedule" operation).
  • SHARED (formerly GLOBAL, before art 3.03)
    Defining a service as "shared" in scope gives a guarantee of thread saftey: the service may be called from anywhere at any time simultaneously and continue to behave correctly.

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

There are two constructor signatures supported by the art framework:

MyService(fhicl::ParameterSet const&, art::ActivityRegistry&);  // Used when registering callbacks
MyService(fhicl::ParameterSet const&); 

The service is provided at construction time with access to its configuration parameters and (optionally) 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.