LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

Waking up from SleepEx()

I have a new twist on a question that I have seen posted here a few times as to how to best suspend execution while still maintaining good response to various events.

I have a typical main thread that handles the user interface panel and initiates execution of tests. Thread2 handles UUT communication using an asynchronous timer.

During some phases of UUT testing the main thread is made idle using a loop that includes the SDK SleepEx() function and ProcessSystemEvents().

Thread two occasionally gets data packets that need to be displayed by controls on the main panel created in the main thread. The updates come in bursts of 20-100 packets, at around 20 mS between for each packet. The bursts are spaced 10s of seconds apart so we have intense periods of activity followed by long periods of communication silence.

While it may seem that trying to update the screen at rates of 20mS makes little sense, in this case packet data may be persistent for 4 or more packets. With a high refresh rate of 20mS, the changing data is very visible to the operator on the user screen.

It is not a problem to update the value in the controls on the main panel from thread2, but getting them to redraw requires that that the main thread "wake up" and ProcessSystemEvents(); Setting the main idle loop to something like Sleep(20mS) followed by ProcessSystemEvents(), works, but the main idle loop is forced to wake up frequently when most of the time there is nothing to do. We have found that good user input response does not require anything near this fast of a call rate to ProcessSystemEvents(), so we would like to avoid it if necessary. There can be a lot of other activity on this system so minimizing cpu load in any way helps.

My question is, when SleepEx() is made "Alertable" it can be awakened by an Asynchronous Procedure Call (APC), how can I wake the sleeping main thread up when needed from thread 2 and is it practical to do so, or would I be better off having thread2 maintain the display updates of the controls. The second option has it's own drawbacks since this data display area must also be updated by routines in the main thread. If we can wake the thread up from sleep we can use a longer sleep interval and reduce the overhead resources used to maintain the idle itself.

0 Kudos
Message 1 of 10
(5,976 Views)

It seems that a thread safe queue can be a good approach to your problem without need to put a thread to wake. You can configure a thread safe queue to communicate between the aquisition thread and the main thread and use it so that only data to be displayed are put in the queue by the acquisition thread.

In the main thread you can configure a queue reader callback that is fired by the presence of data in the queue: this way, the callback will never be executed unless when it effectively needs to process data and the UIR update is confined in the main thread leaving the other thread free to handle the test.

A discussion on how to manage the thread safe queue can be found in Multithreading overview document in <cvidir>\bin directory.
 
I hope this can help you in your application
Roberto


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 2 of 10
(5,970 Views)

I had not thought about using a thread safe queue to trigger a callback. I have found TSQs to be a very useful feature of CVI. In this particular case since I can post the data from thread2 directly to the controls in the main thread, I could also use PostDeferredCallToThread() to get the main thread to redraw, but a TSQ is a cleaner and more flexible way to go.

The next question would depend on the internal implemenation of the TSQs (which we may need one of the NI gurus with access to the internal architecture to answer), will the TSQ callback (or a PostDeferredCallToThread call) wake up the main thread or will it be dependant on the main thread completing SleepEX() and calling ProcessSystemEvents()

0 Kudos
Message 3 of 10
(5,965 Views)

The thread associated with the callback must call ProcessSystemEvents (or RunUserInterface or GetUserEvent) for the callback to actually get called. You can create a simple wait function that processes CVI events while waiting for a specified number of milliseconds. Here is a simple example:

#include <ansi_c.h>
#include <userint.h>
#include <windows.h>
#include <utility.h>

#define READ_BUFFER_SIZE 10

static int quit = 0;
static unsigned int tsq;

int CVICALLBACK ThreadFunction (void *functionData)
{
 int buffer[5] = {0};
 
 while (!quit)
 {
  CmtWriteTSQData(tsq, buffer, sizeof(buffer)/sizeof(buffer[0]), 0, 0);
  Sleep(100);
 }
 
 return 0;
}

void CVICALLBACK TsqCallback (int queue, unsigned int event, int value, void *cbData)
{
 int buffer[READ_BUFFER_SIZE];
 
 CmtReadTSQData(tsq, buffer, READ_BUFFER_SIZE, 0, 0);
 puts("In TsqCallback");
}

static void WaitWhileProcessingEvents(int waitTime)
{

 // NOTE - GetTickCount wraps every few days!
 // So you need to protect for that to be robust.

 int startTime = GetTickCount();
 
 do
 {
  ProcessSystemEvents();
  Sleep(0);
 } while (GetTickCount() - startTime < waitTime);
}

void main(void)
{
 int threadFunctionId;
 
 CmtNewTSQ(1024, sizeof(int), 0, &tsq);
 CmtInstallTSQCallback(tsq, EVENT_TSQ_ITEMS_IN_QUEUE, READ_BUFFER_SIZE, TsqCallback, 0,
  CmtGetCurrentThreadID(), 0);
 CmtScheduleThreadPoolFunction(DEFAULT_THREAD_POOL_HANDLE, ThreadFunction, 0, &threadFunctionId);
 
 while (!KeyHit())
 {
  WaitWhileProcessingEvents(100);
 }
 
 quit = 1;
 CmtWaitForThreadPoolFunctionCompletion(DEFAULT_THREAD_POOL_HANDLE,
  threadFunctionId, OPT_TP_PROCESS_EVENTS_WHILE_WAITING);
 CmtReleaseThreadPoolFunctionID(DEFAULT_THREAD_POOL_HANDLE, threadFunctionId);
 CmtDiscardTSQ(tsq);
}

 

0 Kudos
Message 4 of 10
(5,950 Views)
Hello, Martin

If you don't want your main thread to be continuously waking up and going back to sleep, you can use the SDK MsgWaitForMultipleObjects (with an Event object). and from your thread2 you can post the event that will cause that function to return. There are a couple of caveats with this: for one, the implementation of MsgWaitForMultipleObjects if probably not very different from the code that Mohan posted, in that it still has to "wake up" the thread periodically to see if the function should return. But because it is happening at a lower level, it might use less cpu load. And also, it is generally not a good idea for threads that own windows to be sleeping indefinitely. And your main thread owns windows. The reason for this is that sometimes other threads need to send messages synchronously to every window in the process, or even in the system, and if the thread of one of those windows is asleep, it might cause a deadlock.

One other point... there are many UI library functions that will update the display without requiring events to be processed by your main thread. SetCtrlVal is one of them, as are all the plotting functions, for example. Is it feasible that you could tweak your UI update code to only use functions that do not require you to call ProcessSystemEvents from the main thread? (Again, keep in mind that it's generally a bad idea to sleep indefinitely, but you could be sleeping in 100ms chunks, or so,  and that shouldn't be a problem.)

Luis
NI
0 Kudos
Message 5 of 10
(5,944 Views)
Just to emphasize, the thread safe queue callback will not "wake" up the main thread from SleepEx - it does not do any APC. You may want to consider using WaitForSingleObject in the main thread and signal it using a Win32 event from the worker thread whenever you need it to wake up - you would not need to use the CVI thread safe queues in this case.
0 Kudos
Message 6 of 10
(5,937 Views)
This is just a conceptual question: which is the benefit of putting the main thread to sleep with SleepEx?
 
Till now I hane structured my applications in a manner so that when a test is in execution the main thread is idle in RunUserInterface statement in the main function. I obtain it hiding or disabling all controls on the panel so that no user interaction is possible; on the other hand, the main hread is still "alive" so that TSQ events or PostDeferredCalls or so are still handled. This assumiing the resources spent by the main thread in this configuration are very little.
 
Now this thread puts a big question mark in my brain: can I gain something in putting the main thread to sleep? And what? And when this approach is preferable to my one? And what are the negative issues in doing so?
 
Any hint or a link to any documentation is appreciated.
Roberto


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 7 of 10
(5,927 Views)

Luis and Mohan,

Thanks for pointing me in the direction of WaitForSingleObject() and MsgWaitForMultipleObjects(). Trying to alert the sleeping thread by queuing an APC was not getting me anywhere. In this case an implementation based on MsgWaitForMultipleObjectsEx() does exactly what I needed.

That is a good point about using SetCtrlVal(). That could definitely be useful in some situations. In my particular case I have a large number of controls that are updated at a very fast rate for a short period of time. I have found I can achieve very good screen update performance using SetCtrlAttribute() ATTR_CTRL_VAL and a final call to ProcessDrawEvents().

100mS was my target sleep interval. Using shorter intervals with Sleep() in a loop was not really the right answer for this particular application. As you point out, putting the main thread to sleep for a period longer than 100mS would undoubtedly be a bad idea. But the MsgWaitForMultipleObjects() technique would probably be very useful for making a more dedicated worker thread idle while having better wakeup response than a simple Sleep() and ProcessSystemEvents() loop. In my experimenting with this it looks like time periods in worker threads of 500mS are not an issue.

For others who come across this thread I think Mohans code is an excellent example of how to implement a wait or idle function while still maintain good response to both the user interface and a background communication thread. If it where not for the odd response and timing requirements of my application I am sure this is the type of implementation I would have used.

0 Kudos
Message 8 of 10
(5,904 Views)

Roberto,

My case is somewhat unique in that I must release as much of the system resources as possible back to the CPU so that another (less efficient) application can meet its own performance requirements. The unique situation here is that my application must remain idle at a very low cpu load rate for indefinite periods which can be short or long. This is combined with a requirement for a very short wakeup response period. Upon being awakened it needed to update the display at an unusually fast refresh rate. This is not a requirements combination I have come across that often so I suspect your normal implementation is more than adequate.  I have made a thread idle before, but never needed to wake up in such a short interval or repeat the operation in such rapid succession 

I really do appreciate the input from yourself, Luis and Mohan on this. This is the kind of discussion that really helps to expand my understanding of the details that make specific functions more suitable to a task than others and greatly increases my knowledge of how to interface the SDK with CVI.

0 Kudos
Message 9 of 10
(5,905 Views)

Ah, so now I understand what's the basic reason for such a structure. You're right that I never had such an application before, but it's always good to know some else's problems and possible solutions since you never know what you will need to do tomorrow.

And you're right: this kind of discussion is not valuable in how helpful can be in deep into programming special applications. I have found several suggestions to face my problems without even needing to post a question!

Good luck with your work Smiley Wink

Roberto



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 10 of 10
(5,893 Views)