04-07-2021 02:43 PM
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;
}
04-07-2021 02:44 PM
Forgot to post the output
04-08-2021 08:42 AM - edited 04-08-2021 08:46 AM
This is quite clear if you consider things as they are happening:
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
04-09-2021 08:19 AM
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;
}
04-09-2021 09:33 AM - edited 04-09-2021 09:34 AM
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.