The C example emulates the Java™ example. While the native C code base is entirely single-threaded, it is possible to write platform-specific code in which threads are created. In this example of a user-written queue manager activate rule, a thread is spawned which loops, sleeping for a period of time defined in a triggerInterval variable and then, providing it has not been asked to stop, checking that we are in a cheap rate period prior to attempting to trigger transmission. Data, which is required between rules invocations, is stored in the rule's private data structure. The queue manager's close rule function is used to provide the thread's terminating condition, setting a boolean switch, carryOn to MQE_FALSE. This switch can be initialized to MQE_TRUE in the rules initialization function. This function waits until the thread is suspended before passing control back to the application.
The private data structure passed between rule invocations is as follows:
struct myRules_st_ { // rules instance structure MQeAdministratorHndl hAdmin; // administrator handle to carry around between // rules functions MQEBOOL carryOn; // used for trigger transmission thread MQEINT32 triggerInterval; // used for trigger transmission thread HANDLE hThread; // handle for the trigger transmission thread }; typedef struct myRules_st_ myRules; The queue manager activate rule: MQEVOID myRules_activateQueueManager( MQeRulesActivateQMgr_in_ * pInput, MQeRulesActivateQMgr_out_ * pOutput) { // retrieve exception block - passed from application MQeExceptBlock * pExceptBlock = (MQeExceptBlock *) (pOutput->pExceptBlock); // retrieve private data structure passed between user's rules invocations myRules * myData = (myRules *)(pInput->pPrivateData); MQeQueueManagerHndl hQueueManager; MQERETURN rc = MQERETURN_OK; rc = mqeQueueManager_getCurrentQueueManager(pExceptBlock, &queueManager); if(MQERETURN_OK == rc) { // set up the private data administrator handle using the retrieved // application queue manager handle. This is done here rather than in // the rules initialization function as the queue manager has not yet been // activated fully when the rules //initialization function is invoked. rc = mqeAdministrator_new(pExceptBlock, &myData>hAdmin,hQueueManager); } if(MQERETURN_OK == rc) { DWORD tid; // Launch thread to govern calls to trigger transmission myData->hThread = (HANDLE) CreateThread(NULL, 0, timeToTrigger, (MQEVOID *)myData, 0, &tId); if(myData>hThread == NULL) { // thread creation failed SET_EXCEPT_BLOCK(pExceptBlock, MQERETURN_RULES_ERROR, MQEREASON_NA); } } }
The timeToTrigger function provides the equivalent functionality of the run() method in the Java example. Notice the use of the private data variable carryOn, type MQEBOOL, as one of the conditions for the while loop to continue. Once this variable has a value of MQE_FALSE, the while loop will terminate, causing the thread to terminate when the function is exited.
DWORD _stdcall timeToTrigger(myRules * rulesStruct) { MQERETURN rc = MQERETURN_OK; MQeQueueManagerHndl hQueueManager; MQeExceptBlock exceptBlock; myRules * myData = (myRules *)rulesStruct; SET_EXCEPT_BLOCK_TO_DEFAULT(&exceptBlock); /* retrieve the current queue manager */ rc = mqeQueueManager_getCurrentQueueManager(&exceptBlock, &hQueueManager); if(MQERETURN_OK == rc) { /* so long as there is not a grave internal error and the termination condition has not been set */ while(!(EC(&exceptBlock) == MQERETURN_QUEUE_MANAGER_ERROR && ERC(&exceptBlock) == MQEREASON_INTERNAL_ERROR) && myData->carryOn == MQE_TRUE) { /* Are we in a cheap rate transmission period? */ if(timeToTransmit()) { /* if so, attempt to trigger transmission */ rc = mqeQueueManager_triggerTransmission(hQueueManager, &exceptBlock); /* wait for the duration of the trigger interval */ Sleep(myData->triggerInterval); } } } return 0; }
The timeToTransmit() function returns a boolean to indicate whether or not we are in a cheap transmission period:
MQEBOOL timeToTransmit() { SYSTEMTIME timeInfo; GetLocalTime(&timeInfo); if (timeInfo.wHour >= 18 || timeInfo.wHour < 9) { return MQE_TRUE; } else { return MQE_FALSE; } }
It would probably be a better idea to define constants for the cheap rate interval boundary times and carry these around in the rules private data structure also but that has been not been done here for reasons of clarity.
The function returns MQE_TRUE to suggest that we are in a cheap rate period, that is between the hours of 18:00 and 09:00. A return value of MQE_TRUE is one of the prerequisites for transmission to be triggered in timeToTrigger(). Finally, the queue manager close rule is used to terminate the thread. Notice that one of the conditions for termination of the timeToTrigger() function is for the boolean variable carryOn to have a value of MQE_FALSE. In the close function, the value of carryOn is set to false. But, there may still be a considerable lapse of time between when this value is set to MQE_FALSE and when the timeToTrigger() function is exited. The value of triggerInterval + the time taken to perform a triggerTransmission operation. Also, we wait for the thread to terminate in this function. We also call triggerTransmission() one more time in case there are still some pending messages.
MQEVOID myRules_CloseQMgr( MQeRulesCloseQMgr_in_ * pInput, MQeRulesCloseQMgr_out_ * pOutput) { MQERETURN rc = MQERETURN_OK; MQeQueueManagerHndl hQueueManager; myRules * myData = (myRules *)pInput->pPrivateData; DWORD result; MQeExceptBlock exceptBlock = *((MQeExceptBlock *)pOutput->pExceptBlock); SET_EXCEPT_BLOCK_TO_DEFAULT(&exceptBlock); // Effect the ending of the thread by setting the MQEBOOL continue to MQE_FALSE // This leads to a return from timeToTrigger() and hence the implicit call // to _endthread myData->carryOn = MQE_FALSE; /* wait for the thread in any case */ result = WaitForSingleObject(myData->hThread, INFINITE); /* retrieve the current queue manager */ rc = mqeQueueManager_getCurrentQueueManager(&exceptBlock, &hQueueManager); if(MQERETURN_OK == rc) { /* attempt to trigger transmission one /* last time to clean up queue */ rc = mqeQueueManager_triggerTransmission(hQueueManager, &exceptBlock); } }