NI TestStand

cancel
Showing results for 
Search instead for 
Did you mean: 

Two parallel executions calling a DLL function

Solved!
Go to solution

Hi,

 

Since it test takes about 6 hours to test my UUT, I am considering to use the parallel model in order to test 2 UUTs at the same time in a parallel fashion.

I am implementing the test code as a CVI DLL project.

 

However, to my surprise, it seems like the steps that call a DLL function actually run in a serial fashion, not in parallel:

 

Of the 2 test sockets, if the first one enters and runs a DLL function, the other one waits for the first to complete its operation and return. Then the second one executes on the same copy of the DLL, so that the global variables in the DLL are actually shared between the executions.

 

So if a DLL step takes 5 minutes to complete, two executions running it in parallel take 10 minutes. This is not a parallel execution in any sense.

 

What I desired, and also expected from TestStand, was to isolate the two executions' DLL copies completely, such that two test sockets could run the same DLL function at the same time by executiong their copy of the function, completely isolated from the other one.

So they have seperate globals, threads,etc and two parallel sockets take 5 minutes to execute a step, instead of 10.

 

Is such a scenario possible?

If not, how can I run my test in parellel (in a truly parallel fashion) when using 2 test sockets?

S. Eren BALCI
IMESTEK
0 Kudos
Message 1 of 5
(8,091 Views)

I'm not sure why you are seeing the calls being serialized. That should not be happening by default. The default behavior you should be seeing, unless you've enabled the Lock setting on the step or have code in your dll that is serializing things, is that both threads call into the same copy of the dll at the same time. If your dll is not written to handle multiple threads calling into it at the same time, you might need to enabled the lock setting to serialize the calls to avoid race conditions. Ideally your code should be written with as little global data as possible so that that multiple calls into the same dll in parallel can be done safely (i.e. only local and parameter data is accessed inside the dll thus there is separate data for each thread). If your dll accesses global data then you must use synchronization primitives such as the Lock setting on the step or locking code inside of your dll itself in order to avoid race conditions when accessing the shared data.

 

Hope this helps clarify things,

-Doug

0 Kudos
Message 2 of 5
(8,081 Views)

Hi Doug,

 

Thank you for the prompt reply.

I think, first, I need to settle in my mind the primitives of the parallel execution model. Could you please help me in this?

 

First, I want to re-state your sentence to be sure I got you correct: "When we start a 2-socket parallel test in TestStand, and when they enter a CVI DLL step (not necessarily at the same time and assuming step execution is not serialized neither in TS or CVI), they run on the same memory copy of the DLL". Did I put it right?

 

Second, variables. I had thought the sockets execute in their isolated "variable-space". From your answer, I understand, they execute in separate threads, they have separate locals in each function but they access the same globals, right?

 

The DLL functions are getting the socket index as an input parameter. The functions will then arrange the hardware/software resource access according to this value. We tried to write the code keeping the 2-socket execution in mind.

Is this the correct way of distinguishing the executions?

 

Thanks again,

S. Eren BALCI
IMESTEK
0 Kudos
Message 3 of 5
(8,068 Views)
Solution
Accepted by topic author ebalci

1) Yes, multiple executions in TestStand calling into the same dll will be calling into the same memory copy of that DLL. Thus dlls called in this way need to be thread-safe (i.e. written in a way that is safe for multiple threads running the code in parallel). This usually means avoiding the use of global variables among other things. Intead, you can store the per-thread state in local variables within your sequence and pass it into the dll as a parameter as needed. Keep in mind any dlls your dll calls also need to be threadsafe or you need to synchronize calls into those other dlls with locks or other synchronization primitives.

1b) Even if your dlls are not thread safe, you still might be able to gain some benefit from parallel execution by using the Auto scheduling step type and splitting up your sequence into independent sections that can be run in any order. What that will do is allow you to run Test A on one socket and Test B on another socket in parallel, then once they are done then perhaps Test B will run on one and Test A run on the other. That way, as long as each test is independent of the other you can safely run them in parallel at the same time even if it's not possible to run the same test in parallel at the same time (i.e. If you can't run Test A on two sockets at the same time, you might still be able to gain an advantage from parallelism by running Test B in one socket while Test A runs in the other. See the online help for the autoscheduling step type for more details).

 

2) Socket executions (and all TestStand executions really) are separate threads within the same process. Since they are in the same process, global variables in dlls are essentially shared between them. TestStand Station globals are also shared between them. TestStand File Globals, however, are not shared between executions (each execution gets its own copy) unless you enable the setting to do so in the Sequence File Properties dialog.

 

3) Sure, using the socket index as a way to distinguish what data to access is perfectly valid. You just have to be careful that what each thread is doing does not affect the data that the other threads are accessing. For example, If you have a global array with 2 elements, one for each test socket, you can safely use the socket index to index into the array and in this way are not sharing data between the threads even though you are using a global variable, however the allocation of the array must be done up front before the threads start executing, or it has to be synchronized in some way otherwise it's possible to have one thread trying to access the data while the other thread is creating it. Basically, you have to ensure that if you are using global data that the creation/deletion, modification, and access to it in one thread does not affect the global data that the other thread is using in anyway or you have to protect such creation/deletion, modification, and access of the global data with locks, mutexes, or critical sections.

 

Hope this helps,

-Doug

Message 4 of 5
(8,054 Views)

Thanks Doug,

 

That was very desciptive and very helpful.

 

Regards,

S. Eren BALCI
IMESTEK
0 Kudos
Message 5 of 5
(7,964 Views)