LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Imlementing synchronized access to shared objects in LabVIEW OOP

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 (LabVIEW Object Oriented Programming) 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[1,2] 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 few engineers at NI. Let's give NI the power of open source design smile.gif
--
Tomi Maila
Message 1 of 4
(3,306 Views)

Hello Tomi,

 

First, thank you for taking the time to write such a well though-out suggestion.  Are you familiar with the “LabVIEW Object-Oriented Programming: The Decisions Behind the Design” document?  I think the reason we chose to implement a ‘by value’ strategy, is that is more in line with the LabVIEW programming paradigm of dataflow, and would make sense to most of our LabVIEW users.

 


I think your suggestion is interesting, and it does highlight the need to think outside of the conventional LabVIEW box and look to some of the innovative things other languages do.  However, I think we all agree that synchronization takes careful planning and extra work for the programmer.  Even with an ‘ideal’ solution I see no way around this.  For LabVIEW users today, one great way to get synchronized ‘by reference’ semantics with your objects is to use a single-element queue to pass your object.  The queue itself is passed ‘by reference’ and is inherently synchronized!  The does have the disadvantage of adding one more small layer of complexity to your program, but some complexity would have to be introduced in any situation.  The other disadvantage with this is that it is not always an intuitive way to implement your program and requires some amount of LabVIEW knowledge before one would generally come across this technique.

 


In any case, I appreciate the time and effort you put in to your suggestion.  Please make sure that you submit the suggestion formally through the NI Product Suggestion Center so that it can be reviewed by some of the decision makers here.

 

Thanks again,

Travis M
LabVIEW R&D
National Instruments
Message 2 of 4
(3,247 Views)
Travis suggested encapsulating objects in queues and sending the queues around an application. Objects embedded into queues does NOT solve the problem and I cannot recommend their use. The reason is quite simple, queues cannot be casted to more generic class. Therefore you cannot pass an object embedded into a queue to subVIs that take a queue of more generic class as a parameter. You cannot create arrays of these kinds of queued objects from different hierarchy level. You really cannot do object oriented programming with these queued objects even thoug you can pass them around an application. Inheritance is the sole of object oriented programming and you lose it in this method.

The only way you can presently create by reference objects that really act as by reference objects is by embedding the class private data members into a queue and taking care of reference handling inside the class not outside the class. This makes the class more complicated but at least you can use all the properties of object oriented programming.
--
Tomi Maila
0 Kudos
Message 3 of 4
(3,241 Views)

Hummm…  I defiantly see the problem with my suggestion.  However, the suggestion of the queue was more of a way to get ‘by reference’ semantics, and does not really help much with your synchronization of private data while maintaining polymorphism.  I thank you for taking the time to make this detailed suggestion, and encourage you to submit it to the product suggestion center (throw in a link to this DF if you want too).  I’ll check around and see if I can come up with any other ideas, but no guarantees Smiley Sad.

 

Thanks again!

Travis M
LabVIEW R&D
National Instruments
0 Kudos
Message 4 of 4
(3,205 Views)