By convention, xxxFetchAndProcess is the name for the managed resource event handler in a service implementation. This is a callback function that is invoked with the dispatcher when the associated wait object fires.
The event handling initialization takes place in either xxxAttachServiceManager (for multiplexed managed resources) or xxxOpenServiceManager (non-multiplexed managed resources). During initialization, an operating system-specific wait object is registered along with a pointer to xxxFetchAndProcess and a pointer to a data structure that can be used to identify and process the incoming event. xxxFetchAndProcess is where an incoming raw event from the managed resource can be consumed or forwarded (either to another Natural Access service or to a Natural Access application). Understanding the Managed Resource discusses how to determine which events should be consumed and which should be forwarded (or translated and forwarded).
As part of identifying the event, xxxFetchAndProcess must first use the wait object to retrieve the actual raw event. It must then analyze the contents of the event and perform the appropriate processing.
tik service
tikFetchAndProcess gets the raw event and checks it to determine event type. It then calls the correct helper function to process that event. Since the tik server is a multiplexed managed resource, it demultiplexes the event by using the client id value embedded in the event.
<<<< excerpt from tikutils.c >>>>
/*----------------------------------------------------------
Event Processing from TICK Server
- Callbak invoked when a corresponding registered wait
object is signalled.
ctaqueuehd: Queue on which event are to be queued.
waitobj: Pointer to wait object that was signalled.
arg: Pointer to DEVICE object (which was
registered by tikAttachServiceManager as
a "queuecontext").
---------------------------------------------------------*/
DWORD NMSAPI tikFetchAndProcess( CTAQUEUEHD ctaqueuehd,
CTA_WAITOBJ *waitobj,
void *arg ){
TIK_DEVICE_OBJECT *ptikDevice = (TIK_DEVICE_OBJECT *) arg;
TIK_CHANNEL_OBJECT *ptikChannel = NULL;
TIK_RSP_COM_OBJ *rsp = &ptikDevice->response;
TSIIPC_COMMAND_STATUS status = { 0 };
DWORD ret = SUCCESS;
/* Complete IPC read. */
if ( tikCompleteReadServerResponse( ptikDevice )
!= SUCCESS )
{
return ret;
}
/* Get Channel object pointer by indexing into the
* channelTbl array using the client_id field.
*/
if( rsp->client_id < 0 || rsp->client_id > 9 ||
(ptikChannel=ptikDevice->channelTbl[rsp->client_id]) == NULL)
{
return TIKERR_UNKNOWN_SERVER_RESPONSE;
}
/* Log trace for server response if requested. */
if ( *tikTracePointer & CTA_TRACEMASK_DRIVER_EVENTS )
{
if( (ret=dispLogTrace( ptikChannel->ctahd,
TIK_SVCID,
CTA_TRACE_SEVERITY_INFO,
TIK_TRACETAG_RSP,
(void *)(rsp),
sizeof(TIK_RSP_COM_OBJ) )) != SUCCESS )
{
return( ret );
}
}
/* Process incoming message from TICK Server.*/
switch ( rsp->response )
{
case TIKSVR_OPEN_CHANNEL_RSP:
{
ret = tikProcessOpenChannelResponse( ptikChannel );
break;
}
case TIKSVR_STOP_TIMER_RSP:
{
ret = tikProcessStopTimerResponse( ptikChannel );
break;
}
case TIKSVR_TICK_RSP:
{
ret = tikProcessTickResponse( ptikChannel );
break;
}
default:
{
/* Unknown server response. */
ASSERT(FALSE);
break;
}
}
return ret;
} /* end tikFetchAndProcess() */
To forward (or translate and forward) the raw event to another Natural Access Service or to the Natural Access application, use either dispMakeAndQueueEvent or dispQueueEvent.
tik service
This is just one of the event processing functions called by tikFetchAndProcess. For convenience, we have chosen to use dispMakeAndQueueEvent.
<<<< excerpt from tikutils.c >>>>
/*--------------------------------------------------------*/
DWORD tikProcessOpenChannelResponse( TIK_CHANNEL_OBJECT *ptikChannel )
{
TIK_RSP_COM_OBJ *rsp = &(ptikChannel->ptikDevice->response);
DWORD prev_state = ptikChannel->state;
DWORD reason;
DWORD ret;
ptikChannel->ChannelInfo.calls = 0;
ptikChannel->ChannelInfo.ticked = 0;
ptikChannel->ChannelInfo.requested = 0;
ptikChannel->ChannelInfo.remaining = 0;
ptikChannel->ChannelInfo.ms = 0;
strcpy( ptikChannel->ChannelInfo.tick_string, "" );
/* Translate reason code from incoming message to
* associated Natural Access reason.
*/
switch ( rsp->reason )
{
case TIKSVR_CHANNEL_OPENED:
{
ptikChannel->state = CHANNEL_TIMER_IDLE;
reason = CTA_REASON_FINISHED;
break;
}
case TIKSVR_CHANNEL_ACTIVE:
{
ptikChannel->state = CHANNEL_NOT_ALIVE;
reason = TIK_REASON_CHANNEL_ACTIVE;
break;
}
case TIKSVR_CHANNEL_INVALID:
{
ptikChannel->state = CHANNEL_NOT_ALIVE;
reason = TIK_REASON_INVALID_CHANNEL;
break;
}
default:
{
ptikChannel->state = CHANNEL_NOT_ALIVE;
reason = TIK_REASON_UNKNOWN_SERVER_REASON;
break;
}
}
/* If CTA_TRACEMASK_DEBUG_BIT0 is enabled,
* log state transition information.
*/
if ( TRACEMASK_BIT0_SET(ptikChannel->tracemask) )
{
char txt[128] = "TIKSVR_OPEN_CHANNEL_RSP : ";
char *reasontxt = tikTranslateCmdRsp( rsp->reason );
strcat( txt, reasontxt );
if ( (ret=tikLogStateTransition( ptikChannel,
prev_state,
ptikChannel->state,
txt )) != SUCCESS )
{
return ret;
}
}
/* Create and enqueue open service DONE event. */
ret = dispMakeAndQueueEvent( ptikChannel->ctahd,
CTAEVN_DISP_OPEN_SERVICE_DONE,
reason,
TIK_SVCID,
CTA_SYS_SVCID);
return( ret );
} /* end tikProcessOpenChannelResponse() */
Service implementations may need to generate events:
To deliver unsolicited information to the application.
To complete asynchronous API command processing.
An event contains the following information:
typedef struct _DISP_EVENT
{
DWORD id; /* Event code (and source service id) */
CTAHD ctahd; /* CT Access context handle */
DWORD timestamp; /* Timestamp */
DWORD userid; /* User id (defined by ctaCreateContext) */
DWORD size; /* Size of buffer if buffer != NULL */
void *buffer; /* Buffer pointer */
DWORD value; /* Event status or event-specific data */
DWORD extra; /* Extra internal event field */
unsigned objHd; /* Service object handle */
MESSAGE_ADDR addr; /* Source/Destination service id */
} DISP_EVENT;
The dispatcher fills in the timestamp field if it is set to zero. The dispatcher sets the userid field based upon information stored within the context.
The size field can be used to pass service-specific data if no buffer is being passed with the event.
If the event is being generated for completion of an asynchronous API command, (for example: CTAEVN_something_DONE), or if a data buffer is being passed with the event, then special buffer flags must be set in the size field as explained in Unsolicited events containing buffered data and DONE events associated with asynchronous API functions.
See the Buffer management summary section for a summary of buffer management.
If an event contains buffered data, you must indicate to the dispatcher whether the event's buffer is static or dynamic. To do this, set one of two flags in the event's size field:
DISP_EVENT evt = {0};
// STATIC
static char svcBuffer[ MAX_BUFF_SIZE ];
evt.size = MAX_BUFF_SIZE | CTA_INTERNAL_BUFFER_STATIC;
evt.buffer = svcBuffer;
// DYNAMIC
evt.size = MAX_BUFF_SIZE | CTA_INTERNAL_BUFFER;
dispAllocBuffer( &evt.buffer, MAX_BUFF_SIZE );
// Data that is to be delivered in the event can
// now be copied into the buffer.
If the service implementation sets the CTA_INTERNAL_BUFFER flag in the event's size field, Natural Access or the application ensures the proper release of this buffer. The service implementation does not need to perform this task. This process allows a service manager to remain unaware of whether the context on which the service has been opened is being shared. Also, if the context is shared, the service manager can remain unaware of when the Natural Access dispatcher has completed delivery of the event buffer to all attached applications.
Service implementations should use the Natural Access dispatcher function dispAllocBuffer for the event's buffer allocation. Because service implementations can be written in either C or C++, the use of this function ensures that the appropriate function is subsequently used for freeing the buffer.
Parameters to certain service API functions can be asynchronous input buffers. That is, although the function returns immediately, the input buffer is still considered in use by the service implementation until the service implementation generates an associated service DONE event (XXXEVN_SOMETHING_DONE).
Due to the client-server architecture, it is a requirement of the service implementation that this DONE event contain a flag in the size field allowing the underlying Natural Access transfer buffers to be freed. Even though a buffer of data is not being sent in the DONE event, the value of the buffer field in the event structure must be set to the same value originally sent in the DISP_COMMAND message as the asynchronous input buffer.
xxxCmdDoSomething( void *mgrcontext, DISP_COMMAND *cmd )
{
// Assume dataptr1 is an asynchronous input buffer.
// Save input buffer pointer for later
mgrcontext->cmd.bufferSize = cmd.size1;
mgrcontext->cmd.inputBuffer = cmd.dataptr1;
// etc.
}
------------------------------- Later, when operation is complete
xxxFetchAndProcess( CTAQUEUEHD qhd, CTA_WAITOBJ *waitobj, void *arg;
{
XXXMGRCONTEXT *mgrcontext = (XXXMGRCONTEXT *) arg;
CTA_EVENT evt = {0};
if( mgrcontext->cmd.inputBuffer )
{
evt.size = mgrcontext->cmd.bufferSize | CTA_EVT_ASYNC_BUF_IN;
ev->buffer = mgrcontext->cmd.inputBuffer;
}
// etc.
dispQueueEvent( &evt );
}
Note: No special flag needs to be set in the size field when the event's buffer is an asynchronous output buffer.