Jump to content

Rolf Kalbermatter

Members
  • Posts

    3,837
  • Joined

  • Last visited

  • Days Won

    259

Everything posted by Rolf Kalbermatter

  1. That's very nice, but don't hold your breath for it! I'm currently overloaded with more important things.
  2. It probably is, or some of the other methods that start with LvVariant.. But without some header file that documents at least the parameter list this is a painful process to figure out
  3. Interesting info. The pointer size was already mentioned by Shaun, but I have another question or two. Why the initial 1 byte in the class size? Wouldn't that either cause extra alignment in the structure or otherwise unaligned pointer access, which as far as I know is rather inefficient on non x86 systems? Also would the data and typedescriptor by any change be the ILVDataInterface and ILVDatatypeInterface objects documented in the LabVIEW cintools directory? And does the absence of these header files in preLV2009 mean that the actual handling of data was done differently before, or was that already implemented in earlier LabVIEW versions but just not documented in any way? I suppose he means that the handle have to be swapped between your copy of the data and an empty variant with the same type descriptor. Otherwise LabVIEW still sees the data on the return of the CLN and since that wire is not going anywhere, it concludes that the variant refcount can be decremented resulting in a deallocation of the data if their refcount reaches zero so that your copy of the data pointer is then invalid. I'm still not sure this buys us anything in terms of your MoveBlock() (or SwapBlock()) attempt since the variant obviously needs to be constructed somewhere. I suppose that if you store the variant pointer anywhere you would at least have to increment it's refcount too, and if your store the internal data pointer, you would have to increments its own refcount. That would probably mean calling the AddRef() method on the ILVDataInterface pointer inside the variant, if it is an ILFDataInterface pointer and not just the native data only. And the SwapBlock() has the same parameter types as MoveBlock(). And it is not officially documented. What I know is from accidentally stumbling across some assembly statements during debugging.
  4. Well, as far as the LabVIEW code is concerned it is just an anolog read possibly with the use of a FIFO together with a digital read for the encoder. So you will have to find what cRIO module can do the task, based on your requirements for voltage or current range of your sensor and the sample speed you need to get your data with. Then, based on the module type, you look for LabVIEW example VIs that come with the FPGA toolkit. Study them, try to make small changes to them to understand how they work and go from there.
  5. Well the array has to be locked somehow yes. Even in C you would have to do something like this, unless you can limit the possible accesses significantly in terms of who can write or read to a single element. If that can be done then you could get away with referenced access to the array elements with maybe a cas() mechanism to make really sure. But that can have also significant performance losses. The x86/64 cmpxchg is fairly optimized and ideal for this, but the PPC has a potential for performance loss as the syncing is done through storing the address of the protected memory location into a special register. If someone else does want to protect another address, his attempt will overwrite that register and you loose the reservation and will see this after the compare operation and have to start all over again by reserving the address again. The potential to loose this reservation is fairly small as there are only about two assembly instructions between setting the reservation and checking that it is still valid but it does exist nevertheless. The advantage of the PPC implementation is that the reservation does not lock out bustraffic at all, unless it tries to access the reserved address, while the x86 implementation locks out any memory access for the duration of the cmpxchg operation.
  6. Ahh I think you got that wrong. Ever used the inplace structure node? There you have an inplace replace array element without having to read and write the entire array. PPC doesn't have cas directly, but it has synced loads and stores and with a little assembly a cas() can be fairly easily implemented as I have found out in the meantime. As to SwapBlock() I can't remember the details but believe it is more or less an inplace swapping of the memory buffer contents but without any locking of any sort. As such it does more than MoveBlock() which only copies from one buffer to the other, but not fundamentally more. That API comes from the LabVIEW stoneage, when concurrent access was not an issue since all the multithreading in LabVIEW was handled through its own cooperative multithreading execution system so there was in fact no chance that two competing LabVIEW modules could attempt to work on the same memory location without the LabVIEW programmer knowing it if he cared. With preemptive multitasking you can never have this guarantee, as your SwapBlock() call could be preempted anywhere in its executation. One thing about SwapBlock could be interesting, as it does operate on buffers as 4 byte integers if both buffers are 4 byte aligned and the length to operate on is a multiple of 4 bytes.
  7. Well lets forget about the polymorphic data storage aspect for a moment then. That is IMHO a completely separate issue. What you therefore want is a managing infrastructure for the indices and all for both reader and writer but in a way that they are not globally locked but only locally protected. What I didn't like in your example (the first I think, didn't look at the others if there are) was (besides the use of globals of course ) that the implementation of the reader index does absolutely not scale. There is no way to add an additional reader without complete modification of just about anything in there. So I think the first approach would be to have the reader index as an array, and hence why I came up with the reader registration. Now you are right that globals have some sort of protection but only immediate access, you can not have a protected read/modify/store without additional external synchronization means. The question is, do we need protected read/modify/store at all? I think not, as there is only really one writer for every variable and the variables being integers have in itself atomic read access guaranteed on all modern platforms. So what about making the reader index an array? I think that should work. And if you separate the index management from the actual buffer data storage somehow I think that the delays caused by a non reentrant call to the index manager entity (be it an FGV or a LVOOP private data) should not cost to much performance. If protected read/modify/store would be required, maybe the inplace structure node might help. It seems to do some sort of locking that makes sure that the data can not be in an intermediate state. If that fails the only solution I would see in order to avoid explicit semaphores or mutexes would be the use of some external code to access some cmpexch() like function. Incidentally I have been struggling with this type of thing just now for some external code (LuaVIEW for those wanting to know) where I needed to be able to update internal flags without running into a potential race if some other code tries to update another flag in the same data structure. The solution turned out to be the cmpexch() or similar function which is a function that atomically compares the state of a value with an expected value and only updates the value with the new value if that compare is positive. So to set a bit in a word I could then do something like: long atomic_fetch_and_or(long *value, long mask){ long old; do { old = *value; } while (!cmpexch(value, old, old | mask)); return old;} In C this is fairly simple since the cmpexch() (or other names) is a standard OS function nowadays (or usually a compiler intrinsic) but there are exceptions in LabVIEW land such as the older Pharlap based RT targets and also the VxWorks based ones it seems. At least I couldn't find a reliable cmpexch() or similar function so far in the VxWorks headers and Pharlap ETS before LabVIEW 8.5 or so did not have a kernel32.InterlockedCompareExchange This are so called lockfree mechanismes although personally I find that a little misleading since it is actually still locking in the cmpexch() as that normally implements a complete bus lock for the duration of the assembly sequence, to prevent state corruption even when other CPU cores might want to access the same address at that moment. There are variations possible on the type of lock held such as only for write operations or only on read, but they make the whole story even more complex, are rather unportable between different architectures because of differences in their semantic so that I don't think it makes much sense to bother about them for more general purpose use.
  8. Personally I think DVRs are probably the way to go here. They have reference semantics so you won't have to copy all the data each time eventhough I 'm pretty sure the DVR overhead will be negative in case of scalar buffer elements but positive for more complex datatypes. The only external code solution I could think of would be the use of the ILVDataInterface but I'm now pretty sure that is exactly what the DVR actually is internally about and I do not see any benefit in moving that into external code as in both cases you would have to use polymorphic VIs to support multiple datatypes. About the writer needing to know the index of the readers this would seem most easily solved by having all readers subscribe themselves to the buffer and getting back an refnum (index) into an array of indices where the buffer stores the reader index. Each time the reader wants to retrieve its data it then has to hand its refnum and the buffer retrieves the data for the reader and updates the according index. Personally I would use a functional global for the buffer management including the reader indices, but doing it with LVOOP would allow easy instantiation so you can have multiple circular buffers in an application without having to resort to making the FGV itself indexable too.
  9. Since the original post dates from 2009 and the poster since removed his project from his MySpace.
  10. And also only Windows 32 Bit, AFAIK. A more and more important distinction. I thought LabVIEW for Windows 64 Bit uses 8 Byte alignment which is the default for Visual C. So there might be a documentation CAR in order. One thing to mention though: I believe Flatten and Unflatten will go to great lengths to make sure that the resulting flattened byte stream will be compatible across platforms. This includes things like using Big Endian as default byte order, using 128 bit for extended precision numbers on all platforms (even-though most LabVIEW platforms nowadays use the 80 bit extended floating point format of the x86 CPU architecture internally, and a few simply use the double format to make things trivial to port LabVIEW to it) 1), and using a byte alignment of 1 for all elements. And since the binary File read and write supposedly use Flatten and Unflatten internally too, they should also be safe to use for all applications and should be fully platform independent. If they are not then that would be really a bug! The byte alignment as discussed here only comes into play when you pass LabVIEW native data to external code like shared libraries and CINs. Here the data is aligned with the LabVIEW default alignment for that platform. 1) In fact the only platform ever making use of true 128 bit extended floating point numbers was the Sparc platform. But that was not based on a CPU extended floating point format but a Sun software library implementing an extended floating point arithmetics. It was as such quite slow and died completely when the Solaris version of LabVIEW was discontinued. Nowadays extended floating format is really an 80 bit format on most platforms, (and on VxWorks platforms it is internally really just a double format).
  11. Well I"m not sure about multithreading issues with respect to .Net but .Net is more flexible for sure than ActiveX, which was build on OLE with all its pre-Win32 legacy issues. So it does seem reasonable that the .Net component is by default instantiated and run as a separate thread and therefore satisfies the Apartment threading requirements of the ActiveX component, without being locked into the LabVIEW UI thread. How much the .Net component would have to do, to actually make sure it is working like that, I would not know. It is entirely possible that the .Net component has to manage the entire threading explicitly for the ActiveX component.Since the ActiveX component doesn't really count as fully managed service in terms of .Net there will also have to be some explicit managed-unmanaged translation code in the .Net component. Fortunately if you use tlbimp/aximp to create the ActiveX wrapper you should be safe from having to bother about these issues.
  12. I'm almost 100% sure it is not documented. And from the looks of it it won't really help you here. The function linked to by the xnode is the only useful exported function in there. If the xnode doesn't know how to deal with variants to make the xnode interface work with that library function, the library function itself most likely doesn't know either. But can you explain what you try to do? Do you really need the runtime variant type feature of a Variant, or do you just want adaptable code that is fine to get the right datatype at edit time? If the second is true, some polymorphic VIs and possibly the ILVDataInterface in combination with Adapt To Type as Call Library Node parameter type might be enough. Just be aware that ILVDataInterface is only available since LabVIEW 2009.
  13. Well, that made me think! It might be not the LvVariant that is refcounted but the data inside. Take a look at ILVDataInterface.h. I think all the diagram data is probably wrapped in that and refcounted and then the LvVariant is a thin wrapper around that. To manage polymorphic data. The ILVData interfaces in there inherit from IUnknown, most likely the same as the COM IUnknown interface, eventhough NI seems to have gone the path of creating a nidl tool that replaces the midl compiler from MS in order to make it work for non MS-platforms. Quite an undertaking but I would certainly start with the source code from Wine for that, who made a widl tool! While Wine is LGPL, as long as it is only for inhouse use, and I doubt NI ever planned to release that toolchain, this would be basically still be fine.
  14. Well, refcounting is for ease of resource management. You simply follow some well defined rules about when to call AddRef() and Release() and then the last Release() automatically deallocates the object. It controls the lifetime of the object, not of the data in the object. Flattening a Variant basically AddRefs() the object, retrieves the data and stores it in a flattened stream and then calls Release() on the object. If nobody else still has AddRefed() the object, it will now be deallocated. Unflattening does a new() for a Variant object which creates the object with one refcount, then AddRef() and then unflatten and store the unflattened data in the object, then Release() again. Works perfect for COM and why not for LabVIEW Variants? The master pointer is an old concept taken from the Mac OS 7 memory manager. Basically the memory manager maintained an array of master pointers from which handles got allocated. The entry in the master pointer array was the first pointer in the handle, which pointed to the actual memory area. This allowed the memory manager to maintain a preallocated pool of master pointers and swap allocated memory areas between an "allocated" master pointer pool and an "previously allocated" pool when a handle was freed, in order to safe potentially some memory allocations, but also meant that that pool could be exhausted and then you had to call a special function MoreMasterPointers() or similar to extend the pool. The current handle implementation in LabVIEW still inherits some characteristics from this, but as far as I know a handle is not anymore allocated from a master pointer array but instead is just a pointer to a pointer to a memory area. The memory area does actually have a hidden header in front of the handle that stores information about the size of the handle, some flags and at least in some builds of LabVIEW a back pointer to the pointer that points to the memory area. But most of this information is not anymore actively used (except potentially in special internal debug builds to more easily track down memory corruptions).
  15. I don't think so. Handles do not really maintain a ref count in itself. And I doubt a LvVariant is a real handle although it seems to be a pointer to a pointer (or more precisely a pointer to a C++ class). I would suspect the LvVariant to implement some COM like refcounting but would not know how to get the refcount properly increased and decreased.
  16. That is most likely not a real fix but just an avoidance of a symptome. It looks like LabVIEW is actually maintining some form of reference count on Variants. Copying the pointer alone is likely not enough to keep the variant alive, but somehow the reference count has to be incremented too (and properly decremented afterwards again, to avoid lingering Variants.
  17. And why do you think a variant is 8 bytes long? I don't know how long it is, but it is either a pointer or a more complex structure whose size you cannot easily determine. In the pointer case, which I would tend to believe it is, it would be either 4 bytes or 8 bytes long depending on if you run this on 32 bit or 64 bit. The pointer theory is further enforced by the lonely definition of the LvVariant typedef in the extcode.h file. It is certainly an object so will hold a virtual dispatch table (a pointer, yes) and the actual data itself in whatever format LabVIEW would like. Most likely they chose a similar approach to the Windows VARIANT layout with a variable telling the type of the data and the actual data either as pointer for non-scalar data and directly embedded for scalars. If you run your VI on LabVIEW for 32 bit, the second MoveBlock will overwrite data in memory beyond the Variantpointer and therefore destroy something in memory. Please note also that LabVIEW knows in fact two variants. One is the native LvVariant and the other is the Windows VARIANT. They look the same on the diagram and LabVIEW will happily coerce from one to the other but in memory they are different. And while you can configure a CLN parameter to be a Windows VARIANT, this is only supported on Windows obviously. Still wish they would document the LvVariant API that is exported by LabVIEW.exe.
  18. And that is not necessarily LabVIEWs fault. Most ActiveX controls/servers are not designed to be multithreading safe, declaring themselves to be apartment threaded, meaning any specific caller has to call them always from the same thread. LabVIEW being itself highly dynamic threading can only guarantee this by executing them from the only special thread it knows about, and that is the UI thread. Theoretically LabVIEW could of course extend its already very complex threading model by providing a dynamic number of fixed threads (nice contradiction in here ) one for each apartment threaded ActiveX control the user wants to run, but that would make the whole threading in LabVIEW mainly even more complex and more likely to fail under heavy conditions, so it would buy little in the end. The best solution would be to require the ActiveX component developer to provide a free threading compatible component and register it as such in Windows. Then LabVIEW can call it from any of its threads and does not have to push it into the UI thread. But as already pointed out, free threading capable ActiveX components are a very rare species, since they are not exactly trivial to develop and the apartement threading works in the majority of use cases good enough.
  19. Attachment to post on the NI site, as the attachment upload seems borked there. VB VISA.zip
  20. The NIPrivatePtr() macro definitely is a LabVIEW 2012 invention. That didn't exist before. In LabVIEW 8.5 until 2011 this was called LV_PRIVATE_POINTER() and before that PrivatP(). So as you can see the bad guys here are the NI developers, devilish changing macro definitions over the course of decades. The change from PrivateP() to LV_PRIVATE_POINTER() is most likely due to supporting a development environment (like maybe a new MacOSX SDK) that introduced exactly this macro for its own use and then NI has to change its own macros to prevent a name collision. Them forcing parties like Apple or Microsoft to change their macro definitions because of having longer rights on the naming is not an option. The last change looks a bit bogus. Most likely the macro in 8.5 was added by a Microsoft inspired hungarian notion loving guy or gal in the LabVIEW team, while someone in 2012 decided that this notion is evil and changed it to the more Apple like Upper/Lowercase notion, that is used throughout the LabVIEW source in most places (since LabVIEW was originally written on the Mac and then ported to other platforms later). Changing LabPython such that it can compile with all the different LabVIEW cintools header versions is a nice exercise in preprocessor macro magic, and one I honestly do not feel any inclinations to do at this moment. If (and the emphasis is here on IF) I ever would need to recompile LabPython for any reason, I would still need the old definitions since I generally use cintools headers that are based mostly on the LabVIEW 7.1 definitions, with a few minor edits to support 64 bit compilation.
  21. I can assure you that I have not put in any deliberate typo in the source code, both in the C or LabVIEW part. I can't guarantee that something might be messed up somehow because of some settings on my machine at that time, that would not work out of the box on a standard installation, but there was no intention to make LabPython not compile for others. Would be also pretty useless, given that the ENTIRE source code is actually openly accessible. As to why LabPython is the way it is now, that has a long story. The first idea was simply to have a LabVIEW Python Script server so that you could execute Python scripts in the LabVIEW formula script node. While this was a fun idea, the practical usefullness of this solution turned out to be rather limited, since the script text was in fact compiled into the VI and therefore not possible to change at runtime. Looking at it a bit more it seemed quite trivial to simply export some more of the DLL functions and write LabVIEW VIs to access them, to have a truly dynamic script execution. And the dynamic linking to the Python server was a further afterthought to the whole story, since Python had regular updates and each version used a different DLL name. But working with the Python community turned out a bit complicated at that time. While Python both supports embedding other environments into it as well as embedding Python into other systems (like here in LabVIEW) support for the latter was considered highly unimportant and various discussions about improving that interface where shutdown as undesirable. So once I got it working in LabVIEW I sort of lost every drive to do anything more with it. The fact that you seem to be the first noticing a typo (after more than 10 years) only points out that nobody apparently has bothered in all that time to even take a serious look beyond the Python VIs, despite that the entire source code is openly available. Also that typo might be rather something that was 12 years ago not a typo, with Visual C 6.0 and LabVIEW 6.0 cintools.
  22. Tinkering with someone else secrets can really cause paranoia. Up until now it surely wasn't done, but maybe you brought some legal guy on an idea now. I doubt that developers like AQ would ever even consider such a feature, but if suddenly the powers to be, after reading your post, decide that this is a good idea, he can't really tell them no!
  23. It's a long time that I looked at ASIO. It appeared to to be a somewhat old fashioned API that wasn't interfacable to LabVIEW without a good intermediate shared library layer. If it would support low latencies is beyond my knowledge and also if it would be naturally achievable or if the intermediate layer would have to jump through several hoops and loops to support that. MME or more correctly DirectX as is used at least by the second versioun of the Sound APIs can be used for much less than 300 ms latency too, but you have to do some serious effort for that, something the NI developers didn't feel like doing, which is understandable as NI is in the business of selling high quality data acquisition hardware for that purpose. Anyhow that project did finally not go anywhere as the effort for the ASIO interface was considered to expensive in relation to the rest of the project and buying NI hardware was simply cheaper than the combined costs of a high performance ASIO based audio interface hardware and the development cost of the LabVIEW ASIO interface. Also you need to understand that ASIO is some sort of pseudo standard, proposed by one party in the field, with several others adopting it more or less accurately, with the less being in vast majority.
  24. It's not the lack of description in the VIs or such that is feeling wrong, but the kind of post you do. Posting a ZIP file with a single line of text is really not going to make anyone understand what it might be about, and we all are way to busy to have enough time at our hands to download every single piece of file on the net to play around with it. So the natural reaction is: what the heck? When you go back at those posts about LabVIEW scripting you will find that they weren't usually just a ZIP file thrown out to the masses but there was some time invested to write about what it was, why and how. That helps tremendously. I can understand that you are excited about having undigged something and would want to make everybody on earth know what a great achievement that is. But that is not helped by just posting as is, without some extra explanation about what it is. And FPGA is specific enough that the target audience is rather limited anyhow.
×
×
  • Create New...

Important Information

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