Jump to content

Implementing synhronized access to shared objects


Recommended Posts

Hi,

Many objects in object-oriented programming have an identity, such as a file, a front-panel object or a hardware device. These objects cannot be modelled using present LabVOOP objects as LabVOOP objects gets copied as wire is branched; multiple different wires cannot all represent a single object. This issue has been irritating the community of LabVIEW users since the release of LabVOOP a few months ago.

It seems that there is a huge demand for objects with unique identity i.e. by-reference objects in LabVIEW. The central problem why LabVOOP propably doen't have these objects is the difficulty in implementing synchronized access to these objects from multiple parallel threads. The problem of synchronized access can be divided into two different separate topics. First how the sychronization should be implemented in LabVIEW runtime engine. Second how this synchronization mechanism should be visible to the developer. I'd like to start this thread to discuss these two issues.

Synhronization under the hood

Traditionally people talk about locking of an object and about get-modify-set pass when accessing the object. Locking is traditionally done by acquiring a mutex for an object, modifying the object and releasing the mutex so that other threads can access the same object instance. This is how inter-thread synchronization is traditionally done. However, besides the mutex based locking, the computer science community has innovated also different kinds of methods on synchronizing the access to objects.

One way to get object-level synchronization is modify the runtime engine so that it only allows a single method of a synchronized object to run at any time. This mechanism of syncrhonization is implemented in programming languages like O'Haskell, which is a Haskell variant with object orirented features.

Also different transactional mechanisms have been successful. In transactional mechanisms multiple threads are allowed to access a synchronized object simultaneously. As each method accessing an object commits their changes, they verify that no other object has modified the object simultaneously in a manner than would break the transaction. If such a modification has occurred, everything is rolled back. Transactional mechanism do not suit to every possible situation as not everything can be rolled back. For example it's hard to roll back an action that somehow modifies the physical world.

User experience of synchronization

How the synchronization is generally implemented in LabVIEW shouldn't be directly visible to the developer end-user. The developer should understand the general concepts of synchronization to take full advantage of it, but in general the synhronization mechanism should be integrated directly to development environment. There should in general be no need to acquire a mutex by calling acquire mutex node but instead the end-user should be able to specify which data needs synhronized access in more sophisticated way.

In the following I propose a mechanism of integrating the synchronized access of by-ref objects to the development environemnt of LabVIEW. The proposal is very preliminary but I hope it breaks the ice and the community would start innovating in how should NI implement the syncrhonization support in the user interface of LabVIEW.

Wire level synchronization

Only methods can access object private data members. In synchronized access to the object, it's the methods accessing the private data members that need to be synchronized. The private data members are accessed by applying unbundle node to the class wire and data is written back to the object using bundle node.

What I propose is the following. An unbundle node could either be normal or "synchronized". A synchronized unbundle would guarantee the access to the private data members in synchronized manner. All data wires originating from synchronized unbundle would be of synchronized type, in a little similar manner as a dynamic dispatch wire is of special dynamic dispatch type. Such a wire must evetually be connected to a bundle node. When the wire is bundled back to the originating object, the synchronization requirement is released.

These synchronized wires would look somewhat different from normal wires so that the developer instantly knows that the wire is synchronized. The developer can branch the wire, but only one wire branch can own the synchronized type. The developer could easily select which wire would be syncrhonized by Ctrl+clicking the wire. Such a wire can be considered as a combination of a data and a mutex, even though mutexes don't need to be the underlying synchronization method. The wire just guarantees that there is a mechanism in the runtime engine that makes sure the access to the wire data is synchronized.

There is a need to wire data originating from a non-synchronized wire to a synchronized wire so that it can replace the private data member of the class. This is accomplished with a new node similar to bundle node, that would allow replacing the data in a syncrhonized wire with some data originating from a non-synchronized wire.

The synchronized wire can be connected to a front panel controls of special syncrhonized type. This way the synchronized wire can originate from a method and allow passing the synchronized data to the calling VI and back to another method. This is practical for example in a situation when the developer wants to run different analyzes to a data class but don't want to rewrite all the existing data analysis tools as class members. So the developers writes a syncrhonization acquiring getData method that let's the calling VI to access the syncrhonized data. Then the developer passes this data to an analysis VI and passes the result back to a setData method that writes the result back to the class wire.

There will probably be technical problems in allowing the user to connect such a synchronized wire to all existing VIs since these VIs. Therefore the programming model for all nodes that do not support such synchronized wires will be branching the wire and passing the non-synchronized wire branch to the node and then bundling the result back to the synchronized wire.

To increase performance and decrease unnecessary buffer copies when a syncrhonized wire is branched, if the syncrhonized wire continues directly to the new bundle synchronized wire node, no buffer copy is made.

Discussion

The syncrhonized access to a by-ref LabVOOP objects can be implemented in multiple ways by National Instruments. The synchronized access should be divided to two different and independent parts: 1) the user experience of synchronization and 2) the runtime engine synchronization mechanisms. As LabVOOP objects have special properties compared to other LabVIEW data types, optimal user experience can be gained by designing the user experience specifically for LabVOOP objects. From user experience point-of-view this syncrhonization mechanism may not work for other data types. Separating object syncrhonization from synchronization of other data types is advantageous also for other reasons. Due to the fact that object data can only be accessed via object methods, more advanced synchronization methods may be used with objects than can be used with other data types. O'Haskell synchronization implementation is an example of this. Integrating the synchronization directly to the user interface allows NI to change the mehcanisms under the hood, when computer science comes up with more advanced methods. Therefore NI could begin with traditional and quite easy mutex-based synchronization and later move to more advanced perhaps transaction based syncrhonization methods or even combinations of multiple different methods.

I hope this topic generates discussion that would help NI to implement an excellent synchronization mechanism in LabVOOP. I hope that all talented individuals in the community participate this discussion to help NI to reach this goal. I also hope that if you just have time, it would be great if you could surf the computer science resources to find out what kinds of new techniques there exists for synchronizing access to shared resources. A Large community may find much more innovative solutions than a hired few engineers at NI. Let's give NI the power of open source design :)

Link to comment
This issue has been irritating the community of LabVIEW users since the release of LabVOOP a few months ago.

How can it stops itching if you don't stop scratching?

It seems that there is a huge demand for objects with unique identity i.e. by-reference objects in LabVIEW

Could you point me to the data that made you reach that conclusion?

Link to comment
How can it stops itching if you don't stop scratching?

Just because you stop scratching doesn't mean it'll heal :laugh:

Could you point me to the data that made you reach that conclusion?

Are you kidding? :blink: Try LAVA, info_LabVIEW and ni.com developmer zone searchess...

Link to comment
Just because you stop scratching doesn't mean it'll heal :laugh:

No it does not...but if you continue you surely won't...

post-731-1160348343.gif?width=400

Are you kidding? :blink: Try LAVA, info_LabVIEW and ni.com developmer zone searchess...

I don't beleive that 20 or 30 (even 100) "power" users qualifies for the majority of users...and when i read a huge demand i expect a huge number of users and if there is a huge number of users i expect it to be the majority...

The majority of LV users try to solve measurement/instrumentation/control problems with a tool named LV...a lot of people (including me) on this forum use that tool in areas it was not initially designed for and expect the tool to fit their needs. If the demand is so huge, don't even have to push for it, NI will deliver it as it will become priority number uno...

LAVA (LabVIEW Advanced Virtual Architects)...i believe that the majority of LV users don't even know what a software architect is really doing on a day by day basis, let alone what a LabVIEW Advanced Virtual Architect does...not even sure i know what a LabVIEW Advanced Virtual Architect is doing...

Link to comment
I don't beleive that 20 or 30 (even 100) "power" users qualifies for the majority of users...and when i read a huge demand i expect a huge number of users and if there is a huge number of users i expect it to be the majority...

I agree here. I believe it was Stephen Mercer that stated that we should think of changes and improvements to LVOOP to happen in years, not months. I think a few people are using LVOOP, and this is one of the places you get a lot of discussion on the topic. Anyway, I believe that by reference OOP is needed, but I also believe that we can do without for right now. As jimi has already mentioned in other places, NI has some other bugs (classes with more than 10 get/set VIs, etc.)to work out before new features start getting added. I also agree with jimi, that, for the most part, the implimentation of the by reference design should be hidden from the programmer / user. I also know, from experience, that to make something good, easy to use, and close to perfect is not as easy as it sounds...

Discussion on this topic is good, and it provides a good resource for NI to use in the future. :thumbup:

Link to comment

LV is going in two directions Jacemdom - it is trying to make life easier for the non-programmers (Express is an example of this) AND it is trying to become more general and advanced so that it is an alternative to the programmers that want to do more (otherwise the first named users may leave LV as soon as their requirements rise - either to outsource the programming or to learn themselves a language that can do the job). Personally I think NI should worry less about lowering the entry level and more about making LV an alternative to textual programming, but NI is also a hardware manufacturer and so they have other things on their mind as well.

Yes, the majority of LV users don't know what a software architect is doing, but you won't find many of those users here Jacemdom. The majority of LAVA users are people that are or want to become power users. OO is an example of something that your non-programming LV user has no need for so the whole discussion about LVOOP will and should be held by "power users", and the majority of those users did not get what they hoped for with LVOOP. To the power user LV is not just a tool - it is G, a language we love to program in and want to see conquer the world! We do not sit on our behinds hoping that NI will someday deliver, we fight for the power of G to grow beyond your "just a tool to solve some measurement/instrumentation/control tasks". We want it to be all it can be!

No it does not...but if you continue you surely won't...

post-731-1160348343.gif?width=400

I don't beleive that 20 or 30 (even 100) "power" users qualifies for the majority of users...and when i read a huge demand i expect a huge number of users and if there is a huge number of users i expect it to be the majority...

The majority of LV users try to solve measurement/instrumentation/control problems with a tool named LV...a lot of people (including me) on this forum use that tool in areas it was not initially designed for and expect the tool to fit their needs. If the demand is so huge, don't even have to push for it, NI will deliver it as it will become priority number uno...

LAVA (LabVIEW Advanced Virtual Architects)...i believe that the majority of LV users don't even know what a software architect is really doing on a day by day basis, let alone what a LabVIEW Advanced Virtual Architect does...not even sure i know what a LabVIEW Advanced Virtual Architect is doing...

Link to comment
To the power user LV is not just a tool - it is G, a language we love to program in and want to see conquer the world! We do not sit on our behinds hoping that NI will someday deliver, we fight for the power of G to grow beyond your "just a tool to solve some measurement/instrumentation/control tasks".

I also share that idea, i only differ on the ways to achieve this. I believe that one should stick to the nature of the tool he is using and LV is a dataflow language that focus on by value design. I also believe that forcing it to be by ref, creates unneeded complexity in the architecture. Dataflow designing/programming brings a new simpler way of solving problems and i don't believe that the methods used to streamline "archaic" textual sequential languages like standard OO should be transfered "textually" to a "graphical" dataflow language. Just like stated in the LabVIEW Object-Oriented Programming: The Decisions Behind the Design document.

One must not forget why OO and all designing methods were created. REUSABILITY...Reusability of code in other code, Reusability of code by other programmers.

This to allow multiple programmers to work on the same design, simpify maintenance, and the ultimate central point of all this...lower the costs!

LabVIEW already had lots of tools to attain those goals, without the need for the added complexity of by ref "unconnected" code", that creates a gigantic number of files enabling the CS programmer to brag about 1000 files just like the old days when they bragged about 1 000 000 lines of code and also have job security by being the only ones who can understand it, especially not the domain expert that he always works for. The way i see things, the simpler the better. I recently changed jobs and got responsability for some LabVIEW apps that had hundreds of files and when i finished with them i had shrunk them by half just by removing the "utility" VIs and files needed to support one of many flavors of "by ref" designs.

It even went to the point that one of the center's clients, who was already doing business with the center i work for, who wanted to start using LabVIEW for its simplicity, was delivered a "by ref" kind of design based on dynamic events, and was seriously considering dropping the project, and also LabVIEW, due to the complexity of the thing. I had to work hard for them to believe me and let me redesigne the app so i could show them that it can really be simple.

We want it to be all it can be!

What i get from this forum is more in the lines of "We want it to be what it is not".

I don't remember who said this or when he said it during NI-Week but i remember the essence of the word :

"It may even be possible with LabVIEW, to give the tools needed by the domain expert to translate is knowledge directly to the computer without the aid of the CS guys" I think it was in the graphical system design summit and i remember seing Jeff Kodosky ther also.

I will have succeeded when i'm no longer needed!

Link to comment
The way i see things, the simpler the better. I recently changed jobs and got responsability for some LabVIEW apps that had hundreds of files and when i finished with them i had shrunk them by half just by removing the "utility" VIs and files needed to support one of many flavors of "by ref" designs.

Well, then it is obvious that you have not coded much with native LVOOP. When using LVOOP, the number of VIs doubles, because for every single class you need a get and a set wrapper due to the protection principle. Pre LV8.2 only needed a bundle/unbundle in these cases. Add reheretence to this, and the amount of VIs doubles again. This isn't neccesarily a bad thing, because when changing something that doesn't work, this assures that you can change only the things you want to change, without touching things that do work.

I think it works like this: The simpler you make things, the harder it gets to change it later. The more granulated, or the more bits and pieces your code consist of, the simpler it will be to change things later. So it is more a question of what kind of simplicity you want. IMO a bi-poduct of LVOOP is poorer performance (mostly due to all the added VIs), and this is very unfortunate because it doesn't have to be like this, if the compiler was more advanced.

Link to comment
Well, then it is obvious that you have not coded much with native LVOOP.

I'm just doing my first exploration of the LVOOP implementation...First steps into LVOOP

i may or may not go that way...one sure thing is that i'm not even thinking about going the by ref/GOOP or any totally disconnected objects way...i already have a simple documented method of designing an easily maintainable/upgradable/understandable large software with recuperable code. So i might just continue developping this method if LVOOP does not fulfill my needs of simplicity/clarity and especially debuggability/easytestability. I know that everything cannot be connected with wires and that sometimes a "shared dataspace" is needed between parallel process, but i create that shared space when i need it and only put the data that needs to be shared there.

As for complex softwares, i think that a high percentage of highly complex softwares are created to solve highly complex real world problems that could be greatly simplified prior to creating the software and thus simplifying the upcoming software to automated the process. Sometimes it's worth questionning "the normal accepted way" instead of accepting it as is and continue building on top of it. Some other time you can only do gymnastics to go put another piece on an already unstable structure. Sometimes this...and sometimes that...and sometimes off...and sometimes under the sun, like for me in 15 minutes... :yes:

Link to comment
I also believe that forcing it to be by ref, creates unneeded complexity in the architecture.

A native referencing system allows to leave a lot of added weight out. You could consider it simpler actually. You would not need to notice that you are actually using a reference. Like in Java. A starter does not see the difference. He does not use the data in parallel at two different places. And if he does, he has a bigger problem with the current system than with a refencing system.

Don't forget young programmers are all used to work with objects. If you tell them that LabVIEW may wait a while before you can execute a method until an other method running in parallel is completed, they may (should :) ) say: "Hey that's great, in C I had to write all that protection code myself." And this behaviour would be perfectly natural, just like a VI can only be executing once at a time.

This document explains a lot. Something that I agree with it that keeping dataflow is important. The argument that it prevents problems when using data in parallel is oversimpling the situation by far and I think it's wrong. For me the document contains no argument against a referencing wire, only arguments for.

"It may even be possible with LabVIEW, to give the tools needed by the domain expert to translate is knowledge directly to the computer without the aid of the CS guys" I think it was in the graphical system design summit and i remember seing Jeff Kodosky ther also.

I will have succeeded when i'm no longer needed!

I think that's what I'm doing in my job constantly. I create the generics (e.g. framework), others use them (And me myself too, BTW). It's needed because the complexity of a measurement system as a whole is often quite big. But the complexity in specific areas is not very big and other users can handle it there. There is so much stuff in common between the various tests that I only need to write the more complex generic stuff and enable the other users to write the specific code for their test without too much of a hassle. That's the nice thing about power users, they enable others to work with LabVIEW.

Success with your exploration of OO in LV.

Joris

I hope this topic generates discussion that would help NI to implement an excellent synchronization mechanism in LabVOOP. I hope that all talented individuals in the community participate this discussion to help NI to reach this goal. I also hope that if you just have time, it would be great if you could surf the computer science resources to find out what kinds of new techniques there exists for synchronizing access to shared resources. A Large community may find much more innovative solutions than a hired few engineers at NI. Let's give NI the power of open source design :)

I really hope to see more NI designers in the discussion. So far I am quite disappointed by their presence. Stephen fiercely continues to defend his standpoint, which I can appreciate and respect. But it looks as if noone at NI actually thinks what so many of us power users think. Very strange. Maybe NI disallows discussing these issues outside the company, which I could partially understand.

Joris

Link to comment
But it looks as if noone at NI actually thinks what so many of us power users think. Very strange. Maybe NI disallows discussing these issues outside the company, which I could partially understand.

Joris

It never came to your mind that all you "Power users" where not respecting the very nature of the tool you are using? DATAFLOW

As for NI i believe that there are more and more people there who preach OO like they all been taught in school and that it takes a big effort for someone to clear all the OO stuff learned from the first day your PARENTS tell you that you INHERITED everything from them and that you are their CHILD and you are lucky to be in a middle CLASS family living in a first CLASS country, to finally grasp the power and simplicity of dataflow modeling. I also believe that you will have to completely destroy LV if you want a completely by ref LV. I also believe that there are still a lot of DATAFLOW fighters inside NI and to them :worship:

One question to all you "Power users",

Why don't you all stop complaining about what LV should or could offer and do a by ref Graphical Language of your own? You will have to realize someday that you are not asking things in line with the nature of LV and therefore not working for its advancement, but asking for the replacement of its founding concept. And if it ever succeeds, LV will be dead. You should/could/would then call it RefVIEW!

Link to comment
Why don't you all stop complaining about what LV should or could offer and do a by ref Graphical Language of your own? You will have to realize someday that you are not asking things in line with the nature of LV and therefore not working for its advancement, but asking for the replacement of its founding concept. And if it ever succeeds, LV will be dead. You should/could/would then call it RefVIEW!

The by-ref design allready exists in LabVIEW, we have VI-server, queues, notifers, file-I/O, xControl etc. For me it is therefore clear that by-ref can co-exist with the dataflow. The existing by-ref GOOP implementations also shows that OOP is possible with a by-ref approach in LabVIEW.

Whether our by-ref wish is inline with the nature of LV, I leave to others, but since we have a multi threaded system we need synchronization.

A by-ref system that solves synchronization is, in my opinion, easier to debug than a system using globals etc., since I can follow my references to find all access nodes.

My wish, and many others I belive, was that the LVOOP could step in and replace all the GOOP implementations with a native one, that could be optimized in terms of memory and performance. Now I hope that NI will present a synchronization scheme that efficiently can share data between wires and threads. This way we can have simultaneous access to data while still using the powers of LVOOP.

Link to comment

I see others here are doing their best to be polite and treat your views with respect so let me be more blunt - I think you just don't know what you are talking about. You have obviously no understanding of object orientation but you still insist on debating it (with little or no humility). :headbang:

I assume the sewer system analogy is an analogy close to how you view wires in LabVIEW...and you do not understand how we can talk about objects instead of data going through that sewer. The sewer analogy however is way too limited to describe the wire in LabVIEW - LabVIEW already have tonnes of by reference wires which do not fit to that analogy (even though you try your hardest to make it fit). You should in other words be used to think of the wire as something that can send a reference, not always the items (sewage in this case) themselves...

It would be interesting to see how your code looks like - what kind of problems you are dealing with and how you solve it. Unless the problems are of a limited nature I am quite sure we could point out lots of places where you are either using the concepts that you are arguing against (and have to do so), or where you could have achieved much better code and applications if you did (it is not always a question of requirement, but a better way of achieving things or better yet - to achieve better results).

If not I will be the first to congratulate you on teaching me and probably a lot of others a lesson.

It never came to your mind that all you "Power users" where not respecting the very nature of the tool you are using? DATAFLOW

As for NI i believe that there are more and more people there who preach OO like they all been taught in school and that it takes a big effort for someone to clear all the OO stuff learned from the first day your PARENTS tell you that you INHERITED everything from them and that you are their CHILD and you are lucky to be in a middle CLASS family living in a first CLASS country, to finally grasp the power and simplicity of dataflow modeling. I also believe that you will have to completely destroy LV if you want a completely by ref LV. I also believe that there are still a lot of DATAFLOW fighters inside NI and to them :worship:

One question to all you "Power users",

Why don't you all stop complaining about what LV should or could offer and do a by ref Graphical Language of your own? You will have to realize someday that you are not asking things in line with the nature of LV and therefore not working for its advancement, but asking for the replacement of its founding concept. And if it ever succeeds, LV will be dead. You should/could/would then call it RefVIEW!

Link to comment

The discussion in this thread is really out of the topic. The discussion here seems to be if OOP is needed in LabVIEW or not. I especially started a new topic to discuss the ways to implement synchronization of shared resources between multiple threads. I'd appreciate if you dear virtual architects could stick to the topic or at least close to it. If you would like to discuss about OOP in general, please start a new topic for this issue. Especially if you would like to blame one another, start a new topic within the LAVA lounge about why X.X.X. is a bad programmer or what ever. It is very hard for people to find relevant information from this forum if the topic doesn't describe the discussion at all.

Link to comment
It never came to your mind that all you "Power users" where not respecting the very nature of the tool you are using? DATAFLOW

First, i don't consider myself a "power user", i just think LabVIEW is an excellent tool that i use every day for taking measurements, analyzing and displaying data and for general programming. It is only during the last 5 years that i have used LabVIEW as my preferred programming language, before that LV was almost exclusively data acquisition and logging, and used FORTRAN and C/C++ (more C than C++) for other tasks. I am not 100% sure why i "got stuck" with LV, but i think it has something to do with the visual programming, the block diagrams that just fits my brain better than a textual language.

Secondly, what exactly is DATAFLOW? Is dataflow something special? Maybe i think in too simplistic terms here (and get arrested by jimi and aristos :D ), but IMO any language that use call by value exclusively is dataflow. C is natively call by value, and if i use C with only call by value syntax, it becomes dataflow. When using call by value exclusively, all that ever will flow is the data, hence it is dataflow almost per def. Java is also dataflow. There is no other way to program in Java except call by value, untill you use OO where objects are called by ref. Fortran is not dataflow, since it is impossible to use call by value exclusively. So, to say that G is a visual block diagram version of Java where the objects are stripped off, is in fact a very accurate description IMO (although i know a lot of people will disagree).

I have used any of the GOOPs one single time, and that was dqGOOP. It was mostly for fun, and because the dqGOOP wire was more pleasant and consistent (the same color throughout) compared with what the result have been when using ordinary queue. Functionality vise queues would do the exact same thing. I have been more of an LCOD person, but as i recently have discovered, LCODs by ref is in fact just as much a GOOP as any other GOOP. So in reality i have been using GOOP for several years.

The brute force and simplicity of by ref you will only discover when using Fortran. When i discovered that labview actually made copies of large arrays by brancing the wire, i thought that this must be one huge major bug. How was i going to do anything remotely close to efficient when this language actually makes copies of arrays? Wasn't this language supposed to be THE language for data acquisistion and analysis of data? Well, the only way was to use one large loop and shift registers, or - as i found out (much) later, using LCOD principle. But still today, i just don't understand why there is no native by ref (and this has nothing to do about GOOP), why there is no way of making a real global that can be accessed efficiently like i can in both C and Fortran. And today, as then, the only reason i can think of is that Labview is made by software engineers that are more hooked up on satisfying some language philosophy than being aware of, and fixing up, the actual and very misfortunate results of that philosophy in a practical situation. It's almost ironic. The only way to handle arrays effectively in an analysisis, that is one of the main things labview is supposed to do, is to make an object oriented-like construct, that don't natively exist in the language - to make an efficient by ref global, that also do not natively exist. But the main problem is that once we figure this out, and find a working solution, we are are happy and don't think about the shortcomings.

The main point is that all these shortcoming of the language could be removed entirely if there was a native by ref GOOP.

Link to comment

In replying to this thread, let me first of all say "Thank you." This particular thread, though it may have strayed from Jimi's original post, is the most coherent discussion of OO in LabVIEW thus far. And it has highlighted a lot of topics in words I hadn't been able to put forth.

I've been staying out of the by-reference debates for the last couple weeks except on technical points. I've made my opinions known, and I was happy to step back and see what the community proposed. Besides, you guys/gals can be a bit intimidating. Let me tell you, being a LV developer is a lot easier when you just let LV stand on its own behind the corporate mask :ninja: rather than associating your own name on a feature. And the paycheck is the same either way. ;)

What makes me want to wade back into the fray? One singularly interesting comment from JFM:

The by-ref design allready exists in LabVIEW, we have VI-server, queues, notifers, file-I/O, xControl etc. For me it is therefore clear that by-ref can co-exist with the dataflow. The existing by-ref GOOP implementations also shows that OOP is possible with a by-ref approach in LabVIEW.

Whether our by-ref wish is inline with the nature of LV, I leave to others, but since we have a multi threaded system we need synchronization.

A by-ref system that solves synchronization is, in my opinion, easier to debug than a system using globals etc., since I can follow my references to find all access nodes.

I would like to point out that each one of the reference examples is a particular refnum type with a specific set of operations available on that refnum. None of these is an implementation of a generic "reference to data" that lets you plug in your own arbitrary operations. That's a distinction that I want to highlight.

In the discussion about lossy queues the other day on LAVA, user Kevin P had an implementation for lossy queues that involved "try to enqueue the data, if you fail (timeout), then dequeue one element, then enqueue again (this time knowing you have space). " For various reasons, I proposed another solution. "Get queue status, if not have space then dequeue, then enqueue".

Both Kevin P and my solutions DO NOT WORK if there are multiple VIs posting to the same queue. Why? Because:

For example:

The queue has max size of 5. There are currently 4 elements already in the queue.

At time index t, these events happen:

t = 0, VI A does Get Status and the value 4 returns

t = 1, VI B does Get Status and the value 4 returns

t = 2, VI B does Enqueue

t = 3, VI A does Enqueue -- and hangs because there's no space left

Kevin's solution fails for similar reasons.

This is a case where you need a locking level for a whole series of operations. To implement a lossy queue with today's primitives, you need to use Semaphores to prevent ALL accesses to the queue from the time you start the "test queue size" operation (whether you're doing that through Get Queue Status or by enqueing and seeing if you timeout) all the way through your dequeue to make space and the final enqueue.

The Queue and Notifier APIs are very complete reference-based APIs. They have locking exclusions for each queue/notifier for every operation on that queue/notifier. And yet they require a whole new level of locking in order to do this "bunched" operation. Semaphores are what is available today. I've contemplated a "Lock Queue" primitive. What would a "Lock Queue" primitive have to do? Well, it would acquire some sort of lock that would prevent other queue operations from proceeding, and it might return a completely new queue refnum -- one that is to the exact same queue but is known to be the one special refnum that you can pass to primitives and they won't try to acquire the queue's lock. What if a programmer forgot to invoke "Unlock Queue"? His code would hang. Or, for example, the producer loop might acquire the lock and just keep infinitely producing, starving the consumer loop of ever having time to run. So, yes, it could be done, I think, but the burden would be on the programmer to know when he/she needs to lock the queue for multiple operations and remember to unlock it again.

Any reference system must leave the burden on the programmer to handle locking/unlocking. If such facilities are not provided then the refnum will have an inability to string multiple functions together on the same refnum and guarantee that the reference isn't being changed by some other parallel section of code. All the implementations of GOOP I have ever seen have this problem. The naive "just make every method of the class exclusive with regard to every other method for a given piece of data" does not handle this sort of locking (and introduces its own issues when one method wants to call another method as a subVI).

My point in this case is that the existence of refnum APIs in LabVIEW is not sufficient to say that a general by-reference system can be created. I'm certainly not ruling it out. But the "acquire-modify-release" mechanism has to be custom written for all the modifications that the reference's author thinks will ever need to be handled atomically. If the author didn't think of a particular functionality, or didn't get around to including it, then there's no parallel-safe way to do it. LV2-style globals provide safety for whatever operations are in the LV2-style global, but try to string multiple operations together and you have problems. Global VIs guarantee that it is safe to read entire large cluster values from the globals without a write occuring mid-way through the read. But other than this primitive locking, they're wide open. So if you "read the value, increment the value, store the value back in the global," you have problems. Whatever general reference system that LV puts forth someday cannot automatically deal with all the locking issues that everyone seems to think a native reference implementation would handle.

References can co-exist with dataflow, but only if the author of each particular refnum type is very careful about protecting the resources to which the refnums refer. It is not something that can (in the technical engineering sense) be handled automatically. It is, however, functionality that you can build when you construct your particular refnum type -- using native by-value OO to build that refnum. You can create a class that has the behavior you want on the wire, with all the locking and unlocking that you're desiring, with whatever functionality you design into it. References co-exist with dataflow because the refnums themselves are dataflow, and because the authors of the refnum types have tried to provide as complete an API as possible. There is no magick bullet that will make generic references safe for parallel execution -- not in LabVIEW, not in any programming language.

Link to comment

I would ask all people to stop using this thread for Dataflow, OO, By Value, By Ref discussion. I'm in the process of creating a new topic especially for that, that will allow this one to continue its original line (if possible at this point) and will gather all the bits and pieces of the Dataflow, OO, By Value, By Ref discussion that have hijacked this discussion and also other ones. I'm doing it right now and it should take me about 30 to 60 minutes.

Link to comment
I would ask all people to stop using this thread for Dataflow, OO, By Value, By Ref discussion. I'm in the process of creating a new topic especially for that, that will allow this one to continue its original line (if possible at this point) and will gather all the bits and pieces of the Dataflow, OO, By Value, By Ref discussion that have hijacked this discussion and also other ones. I'm doing it right now and it should take me about 30 to 60 minutes.

Perhaps the forum admins could copy/move the stuff here under this new topic?

Link to comment
Both Kevin P and my solutions DO NOT WORK if there are multiple VIs posting to the same queue. Why? Because:

Kevin's solution fails for similar reasons.

It is very difficult to get this kind of parallel access systems right. Locking of objects is tricky. Before you know it the system creates a deadlock. Traditional GOOP (which locks on object level) is sensitive to deadlocks. Example:

1. Method X of object A is called.

2. At about the same time method Y of object B is called.

3. The first method (X) requires to call method P of object B. Object B is locked however. OK no problem, we wait.

4. But then this method (Y) calls method Q of object A. DEADLOCK.

This condition is very hard to predict and a common problem in systems that lock on object level. This example is only the simplest form in which the problem can occur. I am affraid this cannot be prevented by transactions either, because actions on the real-world may have happened already, making a roll-back impossible (someone already mentiond this, jimi I guess ?). And a transaction system is very very complex. Object level locking is a problem.

Any reference system must leave the burden on the programmer to handle locking/unlocking.

But should the system not fascilitate the programmer ?

I see no reason why LabVIEW could not do locking for me. I will explain this more.

My point in this case is that the existence of refnum APIs in LabVIEW is not sufficient to say that a general by-reference system can be created. I'm certainly not ruling it out. But the "acquire-modify-release" mechanism has to be custom written for all the modifications that the reference's author thinks will ever need to be handled atomically.

Why do you think that ? This aquire-modify-release is the same every time. Pure code replication.

Whatever general reference system that LV puts forth someday cannot automatically deal with all the locking issues that everyone seems to think a native reference implementation would handle.

No indeed, but it can handle the 98% standard cases. Simply to prevent modifications that are being made from getting screwed up because two of those action happen in parallel. That's the most common problem.

I think locking on method level is very practical and understandable. My reasoing for that is quite simple: a method is meant to perform an action. For that action it is easy to say if it should lock the data. If you have some tree, and need to count the items in the tree, you (probably) don't need to lock. But if you want to modify an item in that tree, you'd probably better lock it. This is very understandable.

Further you don't want to lock on long actions that call many other short running modifying methods. As long as the short-running methods lock in a safe way, the long-running method does not need to lock at all. It is already safe.

Then, what should you lock while the locking method runs.

The most logical thing is the object, but then you can get deadlocks (as explained above).

For my thesis 4 years ago I created an object system (screenshot). It did locking on repository level. So the whole repository would be locked when a method requested to lock. That sounds like a terrible thing, but in practise it is not so bad. After all, only a limited number of methods are locking methods, most of them don't lock at all. The methods that do lock are usually all shortly running methods, the lock is off again in the blink of an eye. No problem at all. It is the responsibility of the programmer of this system to indicate which methods should lock. Or maybe better the other way round, he should uncheck the "exclusive" checkbox for the methods that don't need locking. This way the system even works without any inconsistencies for a starter. It will then not slow down the slightest bit as long as no methods are executed in parallel. Starters don't often do that.

Please let me know what you think of this.

Joris

Link to comment
Why do you think that ? This aquire-modify-release is the same every time. Pure code replication.

No indeed, but it can handle the 98% standard cases. Simply to prevent modifications that are being made from getting screwed up because two of those action happen in parallel. That's the most common problem.

The problem is not with two actions happening concurrently. The problem is with actions from other parallel threads happening in between successive actions in this thread. There's the rub, and I will contend that it is a lot more significant than just 2% of the problem. Further, there's plenty of situations with read operations where preventing the parallelism would work against the performance of the system.

Link to comment
The problem is not with two actions happening concurrently. The problem is with actions from other parallel threads happening in between successive actions in this thread. There's the rub, and I will contend that it is a lot more significant than just 2% of the problem.

OK so if I understand you correctly you are thinking of something with these actions:

1. Lock the data and do something

2. do something else and and unlock the data

Well if you want to execute these actions you can call these methods in an other method that has locking enabled. After all, this sequence of actions on this object can be considered a new action again. Why not make it a method then ? There must be a reason why you want to leave the data in a locked state to come back to it later and unlock it. What actions would you like to perform that you cannot do in a method ?

If the programmer thinks the locking scheme would hinder performance too much, he could add a semaphore to his private class data and use that to perform his own locking. And disable the method locking.

Further, there's plenty of situations with read operations where preventing the parallelism would work against the performance of the system.

Hmm that was why I spent a couple of words to tell that it was not required to use this locking at all times...

Can you give an example of a situation where you think locking during the method execution does not work ?

Joris

Link to comment
OK so if I understand you correctly you are thinking of something with these actions:

1. Lock the data and do something

2. do something else and and unlock the data

Well if you want to execute these actions you can call these methods in an other method that has locking enabled. After all, this sequence of actions on this object can be considered a new action again. Why not make it a method then ? There must be a reason why you want to leave the data in a locked state to come back to it later and unlock it. What actions would you like to perform that you cannot do in a method ?

Oh, there's nothing you can't do if

  1. you know enough about locking to understand that you need to put in a method
  2. you have permission to add a method to the class. You might not be able to add a method if you don't have the password to the class, the class is a shared component among many groups that cannot be modified, the class has been validated by your ISO9000 inspection and can't change further without having to redo the inspection, or changing the class would change some important aspect of compatitiblity. Other reasons may exist, these are just the ones I can think of off the top of my head

Can you give an example of a situation where you think locking during the method execution does not work ?

The classic example is reading tables of a database. There are tables that are updated only rarely but read from frequently, so to maximize performance, you don't lock the table for a read operation, only for write. Locking on read can make many database projects perform so badly as to be unusable.

Link to comment
The classic example is reading tables of a database. There are tables that are updated only rarely but read from frequently, so to maximize performance, you don't lock the table for a read operation, only for write. Locking on read can make many database projects perform so badly as to be unusable.

I don't think a DB is a good example of LabVOOP locking since you have to acquire lock from the database server and not from LabVIEW runtime unless you have in-memory DB written in LabVIEW. In 99% of the cases related to LVOOP the synchronization is needed for multiple references of an in-memory object.

Link to comment
Oh, there's nothing you can't do if

[*]you know enough about locking to understand that you need to put in a method

That's why I propose to use a simple understandable system where you can lock basically an action, a method you want to perform on the object. It should always be possible to lock in more specialistic ways, but you should have a simple solution for the simple cases. It seems like you don't want to make the distinction between fairly simple operations requiring simple locking and specialistic things that need specialistic locking.

[*]you have permission to add a method to the class.

If you cannot modify a class, isn't there much more you cannot do ? I know you could extending a class, but that does not give you the freedom to change the locking scheme.

The classic example is reading tables of a database. There are tables that are updated only rarely but read from frequently, so to maximize performance, you don't lock the table for a read operation, only for write. Locking on read can make many database projects perform so badly as to be unusable.

Again you don't talk about the option I mentioned twice to disable locking for a specific method. Please take that into consideration.

Joris

Link to comment

I'm late to this party, and this is a reply to Jimi's initial post.

But first a comment to Aristos:

LabVIEW already provides a host of facilities that allow a careless programmer to make it deadlock. Why oh why is adding one more an unthinkable action??? I've made my programs deadlock in plenty of ways, and I don't blame LabVIEW for letting me! Basically, multithreading is hard *even* in LabVIEW!

My non-LabVIEW experience of multithreading is with java, and I found that its facilities do fix most problems; one of my close friends is an expert at multi-process and multi-thread systems, and -- compared to the field of non-experimental languages -- he finds java's facilities pretty fine. I have yet to encounter anything to make me disagree! In any event, when synchronized blocks don't do it you have to proceed to the next step and explicitely use mutexes and notifications -- LabVIEW already has those.

Back to my reply to jimi:

Your special synchronized wire is something quite complicated and unlike typical LabVIEW behaviour.

It could all start with an automatic synchronization behaviour on methods such that, when the compiler finds a bundling operation on object data, the method would automatically synchronize on the object data. Obviously, methods that only read object data would *not* synchronize.

Now, if a programmer chooses, they could turn off the automatic synchronization on any write method; a "synchronization structure" would then appear around the bundling operation. This new type of structure would allow for improved granularity on data synchronization. Tunnels into such a structure would synchronize on the object data wire; whether more synchronization options than that are needed is left for further discussion. Note that since the synchronization is a (rectangular) structure on a (finite) block diagram, there's automatically an exit point.

Now, for more complicated stuff, you've got your mutexes and whatnot, and careful application of all that should manage to cover pretty much all cases. If not, there is probably a need to redesign ;b

Feel free to tear it apart, but that seems good enough to me. As long as I'm allowed to stay away from the underlying implementation details, that is!

Link to comment

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.