Jump to content
drjdpowell

DLLs and Project Instances

Recommended Posts

Question: if I have VIs running under two different projects (thus separate "instances" of LabVIEW) and both VIs are calling into the same dll, is a separate copy of the dll loaded in each instance?  Or is there only one copy of the dll?  

Share this post


Link to post
Share on other sites

I don't know for sure (maybe @Rolf Kalbermatter does) but looking at this KB (https://knowledge.ni.com/KnowledgeArticleDetails?id=kA00Z0000019L5JSAU&l=en-US)

indicates that its basically calling win32 LoadLibrary, and the help for that (https://docs.microsoft.com/en-us/windows/desktop/api/libloaderapi/nf-libloaderapi-loadlibrarya) seems to indicate there can only be one (per process):

 

If the specified module is a DLL that is not already loaded for the calling process, the system calls the DLL's DllMain function with the DLL_PROCESS_ATTACH value. If DllMain returns TRUE, LoadLibrary returns a handle to the module. If DllMain returns FALSE, the system unloads the DLL from the process address space and LoadLibrary returns NULL. It is not safe to call LoadLibrary from DllMain. For more information, see the Remarks section in DllMain.

Module handles are not global or inheritable. A call to LoadLibrary by one process does not produce a handle that another process can use — for example, in calling GetProcAddress. The other process must make its own call to LoadLibrary for the module before calling GetProcAddress.

 

Edited by smithd

Share this post


Link to post
Share on other sites
 

Question: if I have VIs running under two different projects (thus separate "instances" of LabVIEW) and both VIs are calling into the same dll, is a separate copy of the dll loaded in each instance?  Or is there only one copy of the dll?  

As far as Windows is concerned, all the "Application Instances" are still part of the same LabVIEW process. Thus, all application instances (projects) loaded by the same instance of the LabVIEW IDE share the same DLL memory space.

You can have "separate copies" of your DLL by loading each project in a separate instance of the LabVIEW IDE (e.g. 32-bit vs. 64-bit, or 2017 vs. 2018).

I'm not 100% sure about this part, but I think different executables built using the LabVIEW Application Builder run as different processes, so they would have "separate copies" of your DLL. I'm not 100% sure if different executables built using the LabVIEW Application Builder run as different processes or not. I think they do, which means they would have "separate copies" of your DLL.

Edited by JKSH
Clearer phrasing

Share this post


Link to post
Share on other sites
 

I'm not 100% sure about this part, but I think different executables built using the LabVIEW Application Builder run as different processes, so they would have "separate copies" of your DLL.

That much is definite -- processes can't share anything

Share this post


Link to post
Share on other sites
 

That much is definite -- processes can't share anything

As far as LabVIEW is concerned, yes! LabVIEW simply uses the Windows API LoadLibrary() and that only distinguishes based on the DLL name itself. If a DLL with that name is already loaded into the process, even from a different location than what you request at that point, Windows will simply return a handle to that DLL and increment a reference counter for that DLL.

However there is something like SxS (Side by Side Assembly) loading, that despite its names not only works for .Net assemblies but really any DLL. A DLL when compiled can add a manifest to its resources that specifies a specific version for one or more of its dependent DLLs and when Windows loads that DLL it will attempt to honor that version compatibility even if another version of that DLL with the same name  is already loaded. The specifics of how to make that yourself is kind of badly documented and IMHO, while invented to help battle the so called DLL hell (different modules inside an application process having been compiled with different versions of dependent DLLs such as C runtime libraries that are binary incompatible to each other) it usually replaces DLL hell by DLL chaos.

Share this post


Link to post
Share on other sites

Thanks guys,

I've been having intermittent lockup of dll.  This dll is not safe to call from more than one thread at a time, and I had put in protection to prevent that, but I noticed the lockup occurred in two different LabVIEW instances at the same time.  My protection, unfortunately, is confined to a single instance of LabVIEW, and thus couldn't stop this.

Share this post


Link to post
Share on other sites
 

Thanks guys,

I've been having intermittent lockup of dll.  This dll is not safe to call from more than one thread at a time, and I had put in protection to prevent that, but I noticed the lockup occurred in two different LabVIEW instances at the same time.  My protection, unfortunately, is confined to a single instance of LabVIEW, and thus couldn't stop this.

You are running it in the root loop (Orange Node), rather than run in any thread. right?

Edited by ShaunR

Share this post


Link to post
Share on other sites

No, I was trying to avoid blocking the UI thread, as some of the calls take a long time.

Share this post


Link to post
Share on other sites
 

No, I was trying to avoid blocking the UI thread, as some of the calls take a long time.

Unless the DLL is thread safe (which you have said it's not) you must run it inthe root loop (AKA single threaded).

Share this post


Link to post
Share on other sites

The dll uses a global variable, in such a way that it isn't safe to call function in parallel from multiple threads.  Whatever that is called if "single threaded" is the wrong terminology.

Share this post


Link to post
Share on other sites
 

The dll uses a global variable, in such a way that it isn't safe to call function in parallel from multiple threads.  Whatever that is called if "single threaded" is the wrong terminology.

Yes. The only way to guarantee that LabVIEW does not use multiple threads is to use the root loop. Unfortunately that is the same thread that the UI runs in for certain tasks (we all know not to use the native dialogues, for example). Under normal conditions, LabVIEW has a thread pool (sometimes a couple of hundred threads) that it uses and there is no control over which threads in that pool it will use to call a DLL. If the DLL is thread safe then you can choose "Run in Any Thread" and LabVIEW will use whichever thread it deems fit at the time to call the DLL and the DLL is assumed to handle thread locking. If it is not thread safe, then you must select "Run In UI Thread" so that it is always called by the root loop thread (there is only one single thread there).

The easiest analogy to think about it in LabVIEW terms is that when you select "Run in Any Thread", the DLL is behaving like a reentrant clone with the setting "share clones between instancies". You will no doubt be aware that strange results can occur if the reentrant clone with this setting has a shift register as memory inside (akin to your DLLs global). The reason for that behaviour is because you cannot guarantee which which clone, in which order, is used so the state of the internal memory,  is unclear. You obviously don't get a crash when that happens, just strange results but in a DLL you'll get deadlocks, crashes and an assortment of bad behaviour - especially if pointers are involved. The way to get around it in the LabVIEW example is to run it either as a "Preallocate For Each Instance" or a normal, non-reentrant VI. The former is not an available option for DLLs. The latter is the equivalent of setting the DLL to "Run in UI Thread".

Edited by ShaunR

Share this post


Link to post
Share on other sites

Without seeing more of the DLL it is hard to say for sure. But if you talk about two different LabVIEW programs (EXEs or different IDE instances) the DLL is loaded into each instance seperately and no normal global variables will be shared between them. They have no way of accessing each others global variable space as that is what process memory separation is all about.

So if two different instances of a process block somehow inside the DLL there must be something else going on. It could be explicit shareing through IPC (network, shared memory, etc) or it could be through access of a kernel device driver that is of course not running in the process itself but in the kernel and might somehow block resources that the DLL tries to access from the other process and somehow locks up. That would be of course bad programming of the DLL programmer. The DLL should detect that the resource is not available and return with an error instead of just blocking.

Share this post


Link to post
Share on other sites
 

Thanks guys,

I've been having intermittent lockup of dll.  This dll is not safe to call from more than one thread at a time, and I had put in protection to prevent that, but I noticed the lockup occurred in two different LabVIEW instances at the same time.  My protection, unfortunately, is confined to a single instance of LabVIEW, and thus couldn't stop this.

I don't know the exact details, but it looks like you could use a system-wide mutex:

https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-createmutexa

I'm assuming linux has something similar.

 

Or since its long-running, another option would be to do whole thing where you make a "blah.lock" file somewhere, like what git does. But thats clearly iffy.

Share this post


Link to post
Share on other sites
 

That much is definite -- processes can't share anything

Rephrasing for clarity: I'm not 100% sure if different executables built using the LabVIEW Application Builder run as different processes or not

  • Like 1

Share this post


Link to post
Share on other sites

 

 

But if you talk about two different LabVIEW programs (EXEs or different IDE instances) the DLL is loaded into each instance seperately and no normal global variables will be shared between them.

This I know to be true (for executables). However. There is only one LabVIEW run-time per platform (which has the memory manager) so how does that affect processes? Is there a root loop per process or one global root loop?

Edited by ShaunR

Share this post


Link to post
Share on other sites

My vague understanding is that labview exes are basically special zip files with an executable header tacked on with the instruction to load the labview runtime. The runtime then takes over and loads the code. This obviously changes with the fast file format, but the general concept is probably about the same.

Point being, the exe is still a separate OS process, it just happens to immediately load up a copy of the lvruntime dll which can be shared.

  • Thanks 1

Share this post


Link to post
Share on other sites
 

My vague understanding is that labview exes are basically special zip files with an executable header tacked on with the instruction to load the labview runtime. The runtime then takes over and loads the code. This obviously changes with the fast file format, but the general concept is probably about the same.

Point being, the exe is still a separate OS process, it just happens to immediately load up a copy of the lvruntime dll which can be shared.

That is what supposedly happens and fast file format should not have any influence on that. The LabVIEW exe is instantiated as OS process and loads very early on the lvrt.dll into the process space and then hands over control to the runtime dll.

The runtime being a dll is mapped into the process space and the data segments (where globals are located) are copied into newly created memory areas inside the process and from there on the runtime dll is operating as an independant entity from any other LabVIEW exe that may use the same dll. No data sharing between the LabVIEW exes is possible except through explicitly created IPC channels (network, pipes, shared memory, etc) or if you happen to use a zero day vulnerability in Windows process space separation.

Share this post


Link to post
Share on other sites
 

Point being, the exe is still a separate OS process, it just happens to immediately load up a copy of the lvruntime dll which can be shared.

 

 

The runtime being a dll is mapped into the process space and the data segments (where globals are located) are copied into newly created memory areas inside the process and from there on the runtime dll is operating as an independant entity from any other LabVIEW exe that may use the same dll. No data sharing between the LabVIEW exes is possible except through explicitly created IPC channels (network, pipes, shared memory, etc) or if you happen to use a zero day vulnerability in Windows process space separation.

Right. So that means the root loop is per executable instance? Or shared (via some IPC mechanism)? Because I am asking in terms of the LV task scheduler.

Share this post


Link to post
Share on other sites
 

 

Right. So that means the root loop is per executable instance? Or shared (via some IPC mechanism)? Because I am asking in terms of the LV task scheduler.

The root loop is definitely per process. It’s simply the primary thread started up by Windows for a process in which the GetMessage(), TranslateMessage(), DispatchMessage() Windows API calls are made over and over again, with minor LabVIEW specific adaptions. This thread is associated with a hidden window whose window procedure then translates everything into a Platform independant message infrastructure that very much resembles the Mac classic message loop. This is basically the famous root loop with the message procedure in the hidden window being a sort of platform wrapper around it.

Under Windows there comes in a potential extra complication as the OLE marshalling hooks into the process GetMessage() API, completely outside the control of LabVIEW. So if you interface with OLE/COM/ActiveX and to some extend even .Net compenents things can get interesting, 

 

  • Thanks 1

Share this post


Link to post
Share on other sites
 

The root loop is definitely per process. It’s simply the primary thread started up by Windows for a process in which the GetMessage(), TranslateMessage(), DispatchMessage() Windows API calls are made over and over again, with minor LabVIEW specific adaptions. This thread is associated with a hidden window whose window procedure then translates everything into a Platform independant message infrastructure that very much resembles the Mac classic message loop. This is basically the famous root loop with the message procedure in the hidden window being a sort of platform wrapper around it.

That makes a lot of sense. thanks.

 

Under Windows there comes in a potential extra complication as the OLE marshalling hooks into the process GetMessage() API, completely outside the control of LabVIEW. So if you interface with OLE/COM/ActiveX and to some extend even .Net compenents things can get interesting, 

I'll add that to the long, long list of why I ban ActiveX and .NET from my projects. :lol:

  • Haha 1

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.