Services

Services are a publicly available pointer to some object of a nature defined by some ServiceFamily. This is usually an implementation of a class with usable methods, and usually is implemented as a Dispatchable class, and often with an extra function to query its service family. Services are obtained from a ServiceFactory?. Each family of services will usually have a helper class to find the correct service within the family for a given need (i.e. class FileReaderEnum for svc_fileReader)

The pointer issued by the factory may be a single shared pointer given to all requestors or it may be unique to each requestor. The pointer must NOT be deleted when it is to be destroyed. You must notify the issuing factory instead so that it can safely destroy the service. There may or may not be an api to handle this, or you can notify the factory directly

note: BU idea: some kind of magic class template wrapper that is created by the Enum classes, which is just a service * (with an operator->() to autocast to service*) and the * to the factory, so it can be safely deleted and it handles notifying the factory in the destructor.
WaI> See bottom of this page.

Registering Services

There are a few ways to register services with the service manager. Of course, once the service API is available, you can register your services with it directly:

WASABI_API_SERVICE->service_register(new waServiceFactoryT<SVCTYPE, SVCCLASS>)

Note that you register factories?, not a service instance itself. For a single-instance service, use the waServiceFactorySingleT factory. See WasabiServiceFactory? for more on service factories.

In most cases, you will want your service available for the lifetime of the application or component. To make this situation simple, declare a static service module declaring your services. apiinit or waComponentClient will then automatically register your service when the application starts, or the Component? is loaded.

As of [587], the static service manager also allows you to declare the prerequisites for your service module, so that services will be loaded in the correct order. Note that prerequisites are per module, not per service, so it is up to you to load your own services in order. As noted in the example below, it is never neccessary to declare the service API as a prerequisite.

Example usage: place this somewhere (in global scope) in your project. For a Component?, a good place would be with your component declaration.

BEGIN_SERVICES(YourClass_Svc)
  DECLARE_SERVICE(SomeSvcCreator<YourServiceClass1>);  // eg, waServiceFactoryT<YourServiceClass1>
  DECLARE_SERVICE(SomeSvcCreator<YourServiceClass2>);
  DECLARE_SERVICETMULTI(svc_serviceclass, YourMultiInstanceServiceClass1>);
  DECLARE_SERVICETMULTI(svc_serviceclass, YourMultiInstanceServiceClass2>);
  DECLARE_SERVICETSINGLE(svc_serviceclass, YourSingleInstanceServiceClass1>);
  DECLARE_SERVICETSINGLE(svc_serviceclass, YourSingleInstanceServiceClass2>);
//  Optional: Declare service module prerequisites
//    (note: service api will always be present, don't check for it)
BEGIN_SERVICES_PREREQS
  PREREQ_BY_GUID(prereq_svc1_guid)
  PREREQ_BY_GUID(prereq_svc2_guid)
  PREREQ_BY_GUID(prereq_svc3_guid)
END_SERVICES_PREREQS
//  Always:
END_SERVICES(YourClass_Svc, _YourClass_Svc);

The Players

  • A little summary of some of the major classes involved with services:
waServiceFactory:   [waservicefactory*.h/cpp] Manages instances of a service. Registered to the
                    ServiceManager (via api_serviceI) after startup via registerService() in a
                    waComponentClient's constructor.
                    -- Need stuff overidden: --
                    waServiceFactoryBase:     General base service factory. Overide newService() and
                                              delService().
                    waServiceBase:            Just exposes a single service pointer, without factory.
                                              Overide getService().
                    -- Useable out of the box: --
                    waServiceFactoryT:        General waServiceFactory template for multi-instance services.
                    waServiceFactoryTSingle:  Service factory template that holds one copy of a class
                                              and reissues the pointer as needed, with reference counting.

ServiceManager:     [svcmgr.h/cpp] Static class compiled in the application. Critical-Section protected.
                    Deals with waServiceFactory's, not the services themselves.
                       Maps services:
                         service-type (GUID)   --> waServiceFactory*                [services]
                         waServiceFactory*     --> owning waComponent (GUID)        [ownermap]
                         service-GUID (GUID)   --> coresponding waServiceFactory*   [services_by_guid]
                          -- going away?: --
                         service pointer       --> coresponding waServiceFactory*   [lockmap]
                         
api_service:        [api_servicei.h/cpp] Service for managing services. It accesses the ServiceManager.
                    Given to each WaComponent after loading with 
                      WAComponentClient::registerServices(api_service *_serviceapi)

SvcEnum:            [svcenum.h/cpp] Client base-class for enumerating services.
                    (see svcenumt.h/cpp, svcenumbyguid.h/cpp, etc. for concrete examples)
                    Uses api_service (WASABI_API_SVC)

List of services to be implemented

  • 'proc'-style Filesystem for registry access

  • Dumping this here for ppl to see:

Version 3: Now shows how to use it too! I think this will work now.

// svcptr.h
template <SERVICETYPE>
class SvcPtr {
public:
  SvcPtr(waServiceFactory *w) : was(w), svc(NULL), inuse(FALSE) {
    was->registerPtr(this);
  }
  virtual ~SvcPtr() { 
    was->deregisterPtr(this);
  }

  SERVICETYPE &operator ->() { return *getService(); }

  SERVICETYPE *getService() {
    if(!inuse) return NULL;
    if(!svc) {
      if(was) svc = static_cast<SERVICETYPE>(was->getInterface(this));
                    // waServiceFactory::getInterface() returns (void *).
                    // This is the equivalent of the old CastService<T>()
      else return NULL;
    }
    return svc;
  }

  int release() { // return 1 if released, 0 if unable.
    if(inuse) return 0;
    if(svc) {
      was->releaseInterface(svc);
      svc = NULL;
    }
    was->deregisterPtr(this);
    was = NULL; // Service Factory is probably going away. getService() needs to not work.
                // You can always try again by getting your service factory through the API again.
    return 1;
  }

  void use() {
    ASSERT(!inuse);
    inuse = TRUE;
  }
  void done() {
    ASSERT(inuse);
    uses = FALSE;
  }

private:
  waServiceFactory *was;
  SERVICE *svc;
  int inuse;
}

template <T>
class UsingSvcPtr {
public:
  UsingSvcPtr(SvcPtr<T> *_svcptr) : svcptr(_svcptr) {
    svcptr->use();
  }
  ~UsingSvcPtr() { svcptr->done(); }
private:
  SvcPtr<T> *svcptr;
}
#define _USINGSVCPTR(id,x) UsingSvcPtr __U_S_P__##id(x);
#define USINGSVCPTR(x) _USINGSVCPTR(__LINE__, x)
// waservicefactorybase.h
// ** changes/additions here will be reflected in waServiceFactory, waServiceFactoryBaseX, and waService
template <class SERVICETYPE, class SERVICE>
class NOVTABLE waServiceFactoryBase : public waServiceFactoryBaseX<SERVICETYPE, SERVICE> {
public:
  waServiceFactoryBase(GUID myGuid = INVALID_GUID) : waServiceFactoryBaseX<SERVICETYPE, SERVICE>(myGuid) {}
  virtual FOURCC svc_serviceType() { return SERVICETYPE::getServiceType(); }

protected:
// SvcPtr will be declared friend in waServiceFactory to access Dispatchable counterparts
  virtual void *svc_getInterface(SvcPtr *svcptr) {
    ASSERT(ptrs.haveItem(svcptr));
    return newService();
  }
  virtual int svc_releaseInterface(void *ptr) {
    return delService(static_cast<SERVICE*>(static_cast<SERVICETYPE*>(ptr)));
  }
  virtual void svc_registerPtr(SvcPtr *svcPtr) {
    ptrs.addItem(svcPtr);
  }
  virtual void svc_deregisterPtr(SvcPtr *svcPtr) {
    ptrs.removeItem(svcPtr);
  }
public:
  /* also: svc_enumPtrs(int n) (??), svc_getNumPtrs(int n) */
  virtual int svc_releasePtrs() { // tells SvcPtr's to release their services.
    while(ptrs.getNumItems());
      if(!ptrs.enumItem(0)->release()) //they remove themselves by calling deregisterPtr();
        return 0; //failed to release.
    }
    return 1;
  }

protected:
  virtual SERVICETYPE *newService()=0;
  virtual int delService(SERVICETYPE *service)=0;

private:
  PtrList<SvcPtr<SERVICETYPE>> ptrs;
};

Usage:

// Get a service factory: Enum it through the API, or through a helper SvcEnum class,
// however you want.
waServiceFactory *was = WASABI_API_SVC->enumServiceByGuid(mySvcGuid); 
// Then make your SvcPtr.
SvcPtr<mySvcT> svc(was);
// this can be done locally or in a class constructor (for instance for an oft-used service).

// now use your SvcPtr.
svc.use();
svc->mySvcT_foo();
svc.done();

// or: (especially useful in class functions)
{
  USINGSVCPTR(svc) // constructs a UsingSvcPtr, which calls svc.use()
  svc->mySvcT_foo();
} // svc.done() is called on destruction of the UsingSvcPtr.

// Finally, if you're done with the service, you can:
svc.release();
// This is also done on destruction, so is not necessary unless you like to be good to the environment.