LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

What can cause the scheduler to reschedule the same thread(task)?

What can cause the scheduler to reschedule the same thread while the first execution of the thread hasn’t completed?  Along the same lines, what can make a Callback be required to be reentered?  Are there any library calls that it would call?

 

I have a multi-threaded application where I am using Threads created using LabWindows functions.  The three cases that I am wondering about especially in relation to any use of “static variables” are as follows:

  1. A task executes and there is a MessagePopup call.  While awaiting user response, if another invocation of the task occurs, would this same task be called again even though the first execution has not completed?
  2. A task executes and there is a delay within the task.  If another invocation of the task occurs, would the original task be called again even though the first invocation may not have completed.
  3. A task executes.  Is it possible since this is a multi-threaded application that another execution of the same task can be called when the first task has not finished executing?

 

Are callbacks handled differently from thread executions?  I used LabWindows to build the .uir files but I am compiling using Visual Studio because Visual Studio can handle C++ and LabWindows/CVI cannot.  Does this fact come into play regarding the answering of my questions?

0 Kudos
Message 1 of 12
(5,572 Views)
Donna -

The Win32 scheduler (but be careful, the behavior of the scheduler varies somewhat on the various Win32 OS's) schedules a thread for execution according to the scheduling algorithm (priority based), and when a thread is scheduled for execution, it simply picks up where it last left off.  There's no "re-entrancy" in the sense of a function being re-entered.  

It's up to you to ensure that your various threads don't improperly interleave access to a common resource (e.g. a global variable).  Win32 offers any number of mechanisms for this (critical sections, mutex, semaphores, interlocked variables, etc.) The NI libraries are "thread safe" - they serialize themselves, you don't have to do it.

There's really no such thing as a "task" in WIn32 scheduling - Win32 uses the terminology of "process" and "thread".  All scheduling is in terms of threads, regardless of what process they belong to.  The Win32 SDK included with CVI has a good discussion of processes and threads.  The term "task" in computing science generally means a single-threaded process.

There are threading issues unique to VC++.  For example, you shouldn't use CreateThread() in VC++, though it works most of the time 😉  CreateThread() works fine from CVI.  If you're doing an MFC program, AfxBeginThread() does the trick.  If you're non-MFC, _beginthreadex() is the way to go.  And there's the concept of worker threads and user interface threads to understand as well.

Multi-threading in C and C++ on Win32 is way harder than it should be, because neither C nor C++ provides language support for concurrency.   More modern languages do (C#, Java).  It's easy to make a mess if you don't have a sound understanding of the theoretical concepts of concurrency as well as a praqctical understanding of how it's done on Win32.  NI tries to help with threading utility routines, but I find that you generally need to understand the underlying OS mechanisms to be successful.

The book "Multithreading Applications in Win32" by Beveridge and Weiner really helps you understand processes and threads - I recommend it.  It's showing its age but there's no good replacement for it.

Hope this helps, I know it's not a complete answer for you but maybe it will point you in the right direction.

Hayes



Message 2 of 12
(5,553 Views)
Donna,

Those are very good questions, for which there is no universal answer, since the answer depends on the nature of the task is that generated the event. Is this a UI event? A timer event? A DAQmx event? TCP? All of these will have their own distinct behavior.

If you provide some more detail, we'll try to give you a better answer,

The fact that you used Visual C++ to compile should not be a factor. This behavior is specific to the libraries that you are using, both to generate the event, and that handle the calls you make inside the callbacks.

Luis
NI
Message 3 of 12
(5,538 Views)

Hi Luis,

There are two events that we are concerned about.   One is a reader callback from a thread safe queue.  The other is a UI callback.   We are concerned about have to make sure our code is re-entrant.     We know that if there is a MessagePopup call that the callback can be called again while waiting for the popup to be acknowledged (so this code needs to be re-entrant).   What other cases/calls could cause a callback to be called re-entrantly?

Also, we use function CmtScheduleThreadPoolFunctionAdv to create our threads.   We compile under Visual C++ 6.0.   In another post we were told that this could be a problem in this environment.   Are you aware of any issues and if so what are they? 

Thanks,

Donna

0 Kudos
Message 4 of 12
(5,521 Views)
Hello Donna,

The thread safe callback can be reentrant, while you're waiting inside the MessagePopup function. Those events are initiated by something that takes place in a different thread, which is running while the MessagePopup is active. And since MessagePopup processes events, you might end up with nested callbacks in the same thread.

In your previous post you also asked about what happens when the task is busy waiting, as opposed to calling MessagePopup. If the busy wait is caused by the Delay function, for example, or the Sleep function, then you should not receive any other events in this thread, while the function is running. Other functions, such as CmtWaitForThreadPoolFunctionCompletion have this as an option. So, it all depends on the delay.

As for the UI callback, it would probably not be reentrant. An event that is generated from a user clicking on a button, for example, would not be reentrant for the simple reason that the MessagePopup function creates a modal panel, and while a modal panel is active, the user is not able to interact with the main panel, thus the user is not able to generate additional events.
Timer events would not be affected by modality, but timer events (I'm referring to timer controls here, not the asynchronous timers in asynctmr.fp) are also guaranteed to not be reentrant.

It should not matter at all that you compiled CmtScheduleThreadPoolFunctionAdv in Visual C instead of CVI. The only differences with compiling under Visual C is that you use the standard C runtime library from Visual C instead of from CVI, but I don't see how the standard C library (these are functions such as malloc, printf, etc...) would be a factor in this issue.

Luis
0 Kudos
Message 5 of 12
(5,495 Views)

Thank you Luis for your response.  We have to do study some things and we may have more questions regarding your answer.  I do have an additional area that may or may not be related.

When we execute a CmtWriteTSQData call,  sometimes we receive error # -14912  which according to LabWindows online help means "This thread or another thread is currently writing to the thread safe queue."  We stopped in the degger right after the check for the return value of CmtWriteTSQData and when we did a next in the debugger, we received an AcessViolation C00000005.  I have a few questions about this. 

1.  Why is this not handled by Lab/Windows and why is this flagged as an error?

2.  How can I code to prevent this from happening?  I have multi threads running in my application and I don't know how to prevent this condition from happening.

3.   If I have to put in some special code to avoid this type of error from occurring, are there any other error conditions that I would need to put protection code in for?

Thanks,

Donna

0 Kudos
Message 6 of 12
(5,488 Views)
Donna,

Actually the primary purpose of the thread safe queue is to provide data exchange between a single writer thread and a single reader thread. It sounds as if you need to be able to write to a single queue from multiple threads. You can still do this, but you will need to serialize the access to the thread from both writers. You can do this easily using the thread lock functions (CmtNewLock, CmtGetLock, CmtReleaseLock). You can see an example of locks being used in samples\utility\threading\ThreadGuessers\ThreadGuessers.prj

Hope this helps.

Luis
0 Kudos
Message 7 of 12
(5,463 Views)

Luis,

  Thanks for your response.  Would you expect to see an Acess Violation if we did have concurrent access to the same queue? 

Donna

0 Kudos
Message 8 of 12
(5,460 Views)
I really don't know. The error you were getting suggests that CVI was able to detect the situation and return an error, but I can't guarantee that that would always be the case.

Luis
0 Kudos
Message 9 of 12
(5,454 Views)
OK Thanks!
0 Kudos
Message 10 of 12
(5,452 Views)