Registering services

Service registration occurs when the application calls ctaInitialize. Two binding functions are called during initialization:

Service registration

ctaInitialize can only be called once per process; therefore the call will be thread-safe. xxxInitializeManager and xxxDefineService do not need to be coded to be reentrant since ctaInitialize can only be called once per process (and only from one thread within that process).

If the service manager is not loaded, ctaInitialize loads it into memory. Each service manager must reside in its own DLL or library that conforms to the naming convention xxxmgr.dll (for example, tikmgr.dll) or libxxxmgr.so in UNIX.

The dispatcher then calls the service manager entry point xxxInitializeManager.

xxxInitializeManager

xxxInitializeManager is the only required exported function from a service manager DLL or shared library. xxxInitializeManager performs the following functions:

Registering binding functions

The binding function names are registered with the dispatcher during service initialization. This registration makes the binding functions of the service manager available to the dispatcher to be invoked when needed. Binding functions are declared in the CTASVCMGR_FNS function pointer table. A NULL entry in the table indicates that the default implementation for that function should be used (that is, no service-specific implementation of the binding function is required). The following table lists the default implementation of each binding function:

Binding function

Default implementation

xxxDefineService

Return SUCCESS

xxxAttachServiceManager

Return SUCCESS

xxxDetachServiceManager

Return SUCCESS

xxxOpenServiceManager

Return SUCCESS

xxxCloseServiceManager

Return SUCCESS

xxxOpenService

Enqueue CTAEVN_DISP_OPEN_SERVICE_DONE event, reason CTA_REASON_FINISHED

xxxCloseService

Enqueue CTAEVN_DISP_CLOSE_SERVICE_DONE event, reason CTA_REASON_FINISHED

xxxProcessEvent

Return SUCCESS

xxxProcessCommand

Return SUCCESS

xxxAddRTC

Return SUCCESS

xxxRemoveRTC

Return SUCCESS

xxxGetText

Return NULL

xxxFormatMessage

Format event or command by printing the IDs

xxxSetTraceLevel

Return SUCCESS

xxxFormatTraceBuffer

Return SUCCESS

xxxGetFunctionPointer

Return NULL


tik service

<<<< excerpt from tikbnd.c >>>>

 
STATIC CTASVCMGR_FNS tikSvcmgrFns =
{
    sizeof(CTASVCMGR_FNS),
    tikDefineService,        /* xxxDefineService()              */
    tikAttachServiceManager, /* xxxAttachServiceManager()       */
    tikDetachServiceManager, /* xxxDetachServiceManager()       */
    tikOpenServiceManager,   /* xxxOpenServiceManager()         */
    tikCloseServiceManager,  /* xxxCloseServiceManager()        */
    tikOpenService,          /* xxxOpenService()                */
    tikCloseService,         /* xxxCloseService()               */
    NULL,                    /* xxxProcessEvent()               */
    tikProcessCommand,       /* xxxProcessCommand()             */
    NULL,                    /* xxxAddRTC()                     */
    NULL,                    /* xxxRemoveRTC()                  */
    tikGetText,              /* xxxGetText()                    */
    tikFormatMessage,        /* xxxFormatMessage()              */
    tikSetTraceLevel,        /* xxxSetTraceLevel()              */
    tikFormatTraceBuffer,    /* xxxFormatTraceBuffer()          */
    NULL,                    /* xxxGetFunctionPointer() -future */
};

Version and compatibility information

There are two types of version information for a Natural Access service:

The version ID consists of revision information that is intended to be for human-readable identification purposes. The version information consists of a revision number and a build date.

The revision number is reported in the format major_rev.minor_rev (for example, 2.2). The major revision number is typically incremented when a major change in functionality is released. The minor revision number is typically incremented when small changes (for example, bug fixes) are released. Typically, the minor revision number is reset to zero when the major revision number is incremented.

The build date is in mm dd yyyy format. It is typically encoded using the __DATE__ compiler built-in.

Compatibility levels are used for runtime interoperability checking between a target software module and a caller of that software module. For example, the compatibility level of the dispatcher (acting as the target software module) may be checked by a Natural Access service (acting as a caller).

A compatibility level consists of a 1-based integer that is assigned to a target software module. This level is incremented when the software module is modified such that it is no longer compatible with existing clients. Examples of such modifications include:

There are three interoperability scenarios to consider when writing a Natural Access service:

The following table describes processing aspects of these scenarios:

Function call

Assigning a compatibility level

Requesting a compatibility level

Comparing assigned to requested compatibility levels

Dispatcher function called from Natural Access service

Done internally by Natural Access; transparent to service writer.

Use the DISP_COMPATLEVEL macro as the value for reqdisplevel in CTAINTREV_INFO structure

dispRegisterServiceManager compares the internal value of the dispatcher with the value in reqdisplevel. If they are equal, processing continues. Otherwise, processing aborts.

Natural Access service function called from Natural Access application

Set the exapilevel field of the CTAINTREV_INFO structure.

Get the XXXAPI_COMPATLEVEL macro from the target's xxxdef.h file.

Write an application function to call ctaGetServiceVersion for the target service. Compare XXXAPI_COMPATLEVEL with the compatlevel field returned in the CTA_REV_INFO structure.

Natural Access service function called from another Natural Access service

Set the exspilevel field of the CTAINTREV_INFO structure.

Get the XXXSPI_COMPATLEVEL macro from the target's xxxdef.h file. In the client's xxxDefineService function, create a CTAREQSVC_INFO structure and set the target reqspilevel field to the target's XXXSPI_COMPATLEVEL.

dispRegisterService compares the client's reqspilevel with the targets exspilevel. If they are equal, processing continues. Otherwise, processing aborts.


Specifying version and compatibility levels

To specify compatibility levels and version IDs for a service:

The CTAINTREV_INFO structure is defined as follows:

typedef struct            /* CTA internal revision info structure */
{
    DWORD size;           /* Size of the returned structure       */
    DWORD majorrev;       /* Major revision of service/manager    */
    DWORD minorrev;       /* Minor revision of service/manager    */
    char  builddate [12]; /* Build date, "Mmm dd yyyy\0"          */
    DWORD reqdisplevel;   /* Required compat level of Dispatcher  */
    DWORD expapilevel;    /* Exported compat level of Service API */
    DWORD expspilevel;    /* Exported compat level of Service SPI */
} CTAINTREV_INFO;

tik service

<<<< excerpt from tikdef.h >>>>

-----------------------------------------------------
 TIK Version IDs 
    - These ID's are used to identify the revision level of 
      this service. 
 ------------------------------------------------------------*/
#define TIK_MAJORREV                        1
#define TIK_MINORREV                        0
/*------------------------------------------------------------
 TIK Compatibility Level IDs 
    - These ID's are used to determine runtime compatibility
      between the installed TIK service and clients of the TIK
      service. 
    - API_COMPATLEVEL is used by Natural Access application 
      developers who want to check the "hard coded" value in
      their application against the "hard coded" value in the
      installed TIK service
    - SPI_COMPATLEVEL is useful to Natural Access service 
      writers who want to check the "hard coded" value in 
      their client service against the "hard coded" value in 
      the installed TIK service.
  -----------------------------------------------------------*/
#define TIKAPI_COMPATLEVEL                  1
#define TIKSPI_COMPATLEVEL                  1

Initializing global and local tracemasks

Control tracing in one of two ways: with ctdaemon, where a tracemask can be set using the ctdaemon command processor, or with an internal, service-specific tracemask that can be set within an application.

With two mechanisms to control tracing, an application developer is able to:

To determine whether to generate a trace record associated with a trace category, the service must check both the global tracemask and the local (service-specific) tracemask.

To check the global tracemask, the service, upon initialization, must first call dispGetTracePointer to acquire a pointer to the global tracemask. It then stores this pointer so it can be accessed through all service manager and service implementation functions.

tik service

<<<< excerpt from tikbnd.c >>>>

/* ---------------------------------------------
Global service trace mask
-----------------------------------------------*/
volatile DWORD *tikTracePointer;

To define a local tracemask, define a tracemask on a per context basis. A tracemask is defined as a DWORD.

tik service

<<<< excerpt from tikdef.h >>>>

/* ----------------------------------------------------------
   A DWORD tracemask is included in the definition of a
   TIK_CHANNEL_OBJECT since it is allocated (as a mgrcontext) on a
   per CTA context basis.
----------------------------------------------------------*/
typedef struct _TIK_CHAN
{
   DWORD        size;             /* Size of this structure           */
   DWORD        timestamp;        /* Context creation timestamp.      */
   CTAHD        ctahd;            /* Opening CTA context handle       */
   DWORD        channelId;        /* Opened channel number.           */
   DWORD        state;            /* current state of channel.        */
   DWORD        owner;            /* Current owner of channel.        */
   DWORD        tracemask;        /* Context specific tracemask       */
   TIK_DEVICE_OBJECT *ptikDevice; /* Pointer to "tick device" object. */
   TIK_CHANNEL_INFO channel_info; /* Channel-specific info.           */
} TIK_CHANNEL_OBJECT;

xxxInitializeManager example

tik service

<<<< excerpt from tikbnd.c >>>>

DWORD NMSAPI tikInitializeManager( void )
{
    DWORD ret;
    
CTABEGIN("tikInitializeManager");

static BOOL initialized = FALSE;
    if (initialized)
    {
       return CTALOGERROR(NULL_CTAHD, CTAERR_ALREADY_INITIALIZED, TIK_SVCID);
    }
    /* Set manager revision information. */
    tikMgrRevInfo.size = sizeof(CTAINTREV_INFO);
    tikMgrRevInfo.majorrev = TIK_MAJORREV;
    tikMgrRevInfo.minorrev = TIK_MINORREV;
    strcpy (tikMgrRevInfo.builddate, tikBuildDate);
    tikMgrRevInfo.expapilevel = TIKAPI_COMPATLEVEL;
    tikMgrRevInfo.expspilevel = TIKSPI_COMPATLEVEL;
    tikMgrRevInfo.reqdisplevel = DISP_COMPATLEVEL;

    dispGetTracePointer(&tikTracePointer);

    ret = dispRegisterServiceManager("TIKMGR", &tikSvcmgrFns,
               &tikMgrRevInfo);
    if (ret != SUCCESS)
    {
       return CTALOGERROR(NULL_CTAHD, ret, TIK_SVCID);
    }
    initialized = TRUE;
    return SUCCESS;
}

xxxDefineService

After initializing a service manager and its binding functions, services are initialized. The dispatcher initializes each service specified in the call to ctaInitialize by invoking the appropriate xxxDefineService binding function.

xxxDefineService performs the following tasks:

The dispatcher manages parameters for registered services. During service registration (during the call to ctaInitialize), the service must pass the pointer to the parameter description table defined in xxxparm.c to the dispatcher through dispRegisterService.

After the parameter table is registered with the dispatcher, it must not be modified. Each individual parameter descriptor must remain valid and unmodified. All dynamic changes to parameter descriptor tables and their contents must take place before service registration.

tik service

<<<< excerpt from tikbnd.c >>>>

STATIC DWORD NMSAPI tikDefineService( char* svcname )
{
    DWORD ret;
    /*
     * Needed by Natural Access provided error logging 
     * service
     */
    CTABEGIN("tikDefineService");

    /* Set service revision info. */
    tikSvcRevInfo.size = sizeof(CTAINTREV_INFO);
    tikSvcRevInfo.majorrev = TIK_MAJORREv;
    tikSvcRevInfo.minorrev = TIK_MINORREV;
    tikSvcRevInfo.expapilevel = TIKAPI_COMPATLEVEL;
    tikSvcRevInfo.expspilevel = TIKSPI_COMPATLEVEL; 
    tikSvcRevInfo.reqdisplevel = DISP_COMPATLEVEL;
    strcpy(tikSvcRevInfo.builddate, __DATE__);

 
    /* The most important phase of initializing a service! */
    ret = (dispRegisterService("TIK",    
                                TIK_SVCID,
                                "TIKMGR", 
                                &tikSvcRevInfo, 
                                NULL,
                                0,  
                                _tikParmDescTable,
                                NULL));
    if ( ret != SUCCESS )
    {
       return CTALOGERROR(NULL_CTAHD, ret, TIK_SVCID);
    }
    return SUCCESS;
}