LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

TSQ Callback only triggering once

Wondering what I am missing. I am attempting to create a thread whos responsibility is to throw char's onto the console. I created a small sample program (see below) to ensure that I understand the concept. In the program below the TSQCallback is only triggered once. I cannot see what I am missing. I've looked at the examples and don't see anything that I'm not using. 

 

/*---------------------------------------------------------------------------*/
/* Include files                                                             */
/*---------------------------------------------------------------------------*/
#include <cvirte.h>
#include <cvi2009compat.h>
#include <userint.h>
#include <ansi_c.h>
#include <utility.h>


/*---------------------------------------------------------------------------*/
/* Defines                                                                   */
/*---------------------------------------------------------------------------*/
#define MAX_ITEMS_IN_QUEUE 100000
#define MAX_ITEMS_IN_QUEUE_READ_BLOCK 20
#define READ_THREAD_WAIT_TIME 0  

static CmtTSQHandle tsqHandle = 0;    
static CmtTSQCallbackID ThreadPutCharCallbackID;
static char readBuffer [MAX_ITEMS_IN_QUEUE];
static char writeBuffer [MAX_ITEMS_IN_QUEUE]; 


static void CVICALLBACK ThreadedPutChar(CmtTSQHandle queueHandle, 
										unsigned int event, int value, void *callbackData){
	int numItemsRead = 0;
	
	/* Use a buffer to read the data from the queue. */
    numItemsRead = CmtReadTSQData(tsqHandle, readBuffer, MAX_ITEMS_IN_QUEUE_READ_BLOCK, 10, 0);
	
	//printf("Thread read %d chars\nstr:%s\n", numItemsRead, readBuffer);  
	
	//print buffer to stdout
	for(int i = 0; i < numItemsRead; ++i){
		putchar(readBuffer[i]);
	}
}	

int main (int argc, char *argv[]){
	int ret;
	
	//create a TSQ
	CmtNewTSQ (MAX_ITEMS_IN_QUEUE, sizeof (char), OPT_TSQ_DYNAMIC_SIZE, &tsqHandle);
	
	//Trigger the callback to ThreadedPutChat when there are MAX_ITEMS_IN_QUEUE_READ_BLOCK (20)
	CmtInstallTSQCallback (tsqHandle, EVENT_TSQ_ITEMS_IN_QUEUE,
                                   MAX_ITEMS_IN_QUEUE_READ_BLOCK,
                                   ThreadedPutChar, NULL,
                                   CmtGetCurrentThreadID(), &ThreadPutCharCallbackID);
	
	//make a data stream
	for (int j = 0; j < 26; ++j){ 
		writeBuffer[j] = (char) ('A' + j);
	}
	writeBuffer[26] = '\n';
	writeBuffer[27] = '\0';
	printf("writeBuffer: %s", writeBuffer);
	
	//write "stream" into the queue
	for(int i = 0; i < 3; ++i){
		
		ret = CmtWriteTSQData(tsqHandle, writeBuffer, strlen(writeBuffer), TSQ_INFINITE_TIMEOUT, NULL); 

		printf("Added %d items to queue\n",ret);

	}
	
	while(1){
		//Until thread exits 
		ProcessSystemEvents();	
	}
    return 0;
}


 

 

0 Kudos
Message 1 of 5
(1,582 Views)

Forgot to post the output

Richard03xx_0-1617824645592.png

 

0 Kudos
Message 2 of 5
(1,577 Views)

This is quite clear if you consider things as they are happening:

 

  1. You set the TSQ callback to execute in the same thread as the main ()
  2. You write to the queue in a tight for loop that gives no room for other events
    (BTW up to the end of writes the thread is not processing events so reads would not fire even if there were room for them)
  3. Next you enter the final loop that eventually processes events

That final loop executes one read from the queue and no more: as a matter of fact, as you can read in the help for the function (highlight is mine):

 

EVENT_TSQ_ITEMS_IN_QUEUE—A thread safe queue generates this event when both of the following conditions are true:

A thread writes items to the thread safe queue.

The number of items in the queue after the new items are added is greater than or equal to the threshold value.

 

You are no longer writing to the queue, so... no more reads!

It any case, it may be a good habit to read from the queue until it is empty



Proud to use LW/CVI from 3.1 on.

My contributions to the Developer Community
________________________________________
If I have helped you, why not giving me a kudos?
Message 3 of 5
(1,523 Views)

Thanks for the response, 


A few follow up questions, as I still don't quite understand. (Although, the response is thorough). This is my first time threading outside of POSIX so let me know if I am misunderstanding something fundamental. 

 

Intent: I would like the main process to create a child thread. Then, whenever something needs to be printed to the console that thread would handle it. The main process should be able to "send" the thread some chars and move on. The thread should not be busy-looping when there is nothing to be printed.

 

I figured TSQ was the best solution to get the intended results.

 

"You set the TSQ callback to execute in the same thread as the main ()" 
        How? Doesn't CmtInstallTSQCallback() create a thread or do I need to use CmtNewThreadPool() to create the thread and call CmtInstallTSQCallback() inside that?  

 

"You are no longer writing to the queue, so... no more reads!"
        I see, one write == one event. I expected the event to continue while both were true. I didn't expect the write "flag" to be cleared until the queue fell below 20 items. My bad.

Why do I need to call ProcessSystemEvents() in the first place? The point of threading to allow the main process to continue. Is this because there is no actual second thread in this code?

 

 

Thank you, 

Richard

 

P.S. updated code to "work" based on feedback.

 

int main (int argc, char *argv[]){
      
	//... no changes to TSQ code

	//make a data stream
	for (int j = 0; j < 26; ++j){ 
		writeBuffer[j] = (char) ('A' + j);
	}
	writeBuffer[26] = '\n';
	writeBuffer[27] = '\0';
	
	int offset = 0;
	while(1){
		ret = CmtWriteTSQData(tsqHandle, writeBuffer + offset, 1, TSQ_INFINITE_TIMEOUT, NULL); 
		ProcessSystemEvents();	
		offset = (offset >= 27) ? 0 : offset + 1;
	}
    return 0;
}

 


 

0 Kudos
Message 4 of 5
(1,507 Views)

You already are half way in the correct road, there are just a few items to setup before the architecture can work.

 

What you are trying to achieve is the so called producer-consumer paradigm: a thread (producer) makes some data available to another (the consumer) who will get them. Just for clarification, the consumer thread is usually the one that runs the main () and handles all the UI stuff; the other thread is not automagically created: you must create it explicitly with CmtScheduleThreadPoolFunctionAdv (). In this paradigm is correct to install the queue reader in the UI tread as you have already done. The producer thread will call CmtWriteTSQData to send items to the consumer thread.

 

The consumer thread must be processing events for the mechanism to work: the UI thread in CVI is normally sitting in RunUserInterface () waiting for events from the user interface or other sources (timers, interrupts, callbacks or so).

 

You can look at some of the examples that ship with CVI for reference: I suggest you to look at  the one in \samples\utility\threading\ThreadSafQueue\BuffNoDataLoss folder.

 

HINT: to locate an example start the example finder running Help>> Find Examples... menu function, select Search tab and enter "thread" in the search field, next press Enter and locate the desired example in the list on the right.



Proud to use LW/CVI from 3.1 on.

My contributions to the Developer Community
________________________________________
If I have helped you, why not giving me a kudos?
0 Kudos
Message 5 of 5
(1,503 Views)