Actor Framework Discussions

cancel
Showing results for 
Search instead for 
Did you mean: 

ArT_Actors: A Step beyond Actor Framework 3.0?

AristosQueue wrote:

I'm not grasping why you're concerned about the transmission of arbitrary messages by callers. In a non-message passing environment, any method that is public may be invoked by any caller. If a class designer does not wish a method to be universally invokable, the designer should make that method protected/private/etc. With AF, a message can only invoke methods that are either public or community scope. There's no way for a caller to create a message that would call a method that is private. Thus the things that a caller can do to the object via messages is no different than the things that a caller can do to the data directly before the object is launched.


    I agree with “There's no way for a caller to create a message that would call a method that is private”. However, a caller can create a Do payload (calling (only) public Actor methods) resulting in unwanted behavior (blocks Actors message loop for too long, etc.). On top of that (as discussed in Messages 2 & 3 above):

    • A caller message may contain code that does something (malicious or not) else than calling Actor’s private method. When sent from a remote caller (Distributed AF) this opens a vulnerability on the local computer
    • There is a class private data vulnerability in LabVIEW (going back to 8.XX ?) which may not be fixed in LV2013. Most applications may not care about it, but I want to be protected anyway.

If you are concerned about who can send the messages, the AF already limits the senders of messages -- only the caller, the actor itself and its nested actors can send any message.

What if the caller (i.e. Parent Actor) passes Child Actor Queue Refnum to other actors? This may result in Child Actor executing different message sets based on who created it. I may be too entrenched in ‘rigid actors’ mentality (implemented using QDSM) – but I believe that, as an Actor developer, I should have full control over Actor’s functionality. Otherwise I cannot be held responsible for my Actor ‘behaving badly’.

Take a non-actor LV class. Implement all of its methods. This is a pure by-value class that is as fully testable without spinning up the framework as any other. Now change it to inherit from Actor.lvclass. Now it is launchable and messageable to call those methods. Has its testability changed? No. All of those methods are still just as testable. This is why I don't see what you're goal is with separating the Actor into two separate class hierarchies, one for "this is the data" and one for "this is the message handler". If an actor is one that just does data operations upon receipt of a message, then as I see it, the change buys you nothing. But if the actor is one that, as part of handling a message, needs to respond with a message (to itself or to its caller), you've hampered the testability more than you have helped it, at least from my perspective. Can you elaborate on what your goal is with this separation?

Well, the short answer is – ‘Because the Single Responsibility Design Principle says so’. "A class should have only one reason to change"… I think some people refer to this as ‘Separation of Concerns'

A more specific answer: AF Actor class hierarchy has two reasons to change (i.e. expand):

    • Creating child [functional] Actor extensions (the one you call ‘data actors)
    • ‘Decorating’ Actor Core method – adding one or more GUIs and/or one or more  ‘auxiliary loops’

This would be OK if LabVIEW had multiple inheritance, but it does not (that was a prudent decision). So you cant do both at the same time – meaning a ‘decorated’ Actor Core extension cannot be used with all ‘data actor’ extensions.

A standard solution is – decorating one axis of change with the other axis of change – is exactly what I did by separating AF Actor class into ArT_Actor_Class and ArT_Actor_Component_Class. So now you can pass any ‘data actor descendant’ as an argument to any ‘messaging actor descendant’...

I have a real life example (a scanner subsystem), but it is hard to describe in a couple of sentences...

Is there any reason why any given actor could not -- without any change to AF -- encapsulate through containment another object that is its data and get the same separation, if it really is beneficial? Why the need for an explicit "data actor" hierarchy?

That is exactly what I did. But I think you should enforce this separation, so Actor developers will not get into a habit of creating a ‘data actor’ by inheriting from a ‘messaging actor’. In ArT_Actors they cant because these are now two separate class hierarchies...

0 Kudos
Message 21 of 62
(2,134 Views)

AristosQueue wrote:

Regarding "Different Calling Modes for Messages": No, it cannot be solely the decision of the caller whether to wait or not. There can only be waiting messages on one side of the communication channel otherwise you risk deadlock. The whole system has to be designed with the possibility of waiting messages in mind -- it isn't a decision that any single actor can make on its own. Moreover, the goal of the AF is to discourage nearly to the point of waving off completely the use of waiting messages. Designing a system to wait for a *specific* reply and designing a system to wait for *the next message and then do something special when that message happens to be the reply* is different, but is not a significant design challenge. That was the open question -- the second biggest open question -- at the start of the AF project, but at this point, three years later, we've had enough systems built and done enough theoretical work that I can say with fair certainty that Reply Msg should be avoided and when it is used, it has ramifications on the system as a whole, and therefore no single actor should be able to decide it will wait for a given reply on its own.

I must admit that I have mostly used waiting on a reply in adding actors into older non-actor code.  Plus in initialization of subactors (where an actor can't begin full operation until it has properly initialized its subactors).  Staying asynchronous is the general rule.  Note, also, that the way I implement "actors" already has a strong communication asymmetry: the launched actor never has direct access to its Caller's queue, and can't actually explicitly "send" a message to it.  All communication up the hierarchy is by replying to received messages or publishing information that the Caller can subscribe to.  Neither of those two modes supports replys, synchronous or not.  That's why I'm interested in Dmitry's "Topic" design. 

-- James

An old conversation on this for Dmitry's interest if he hasn't read it.   Dmitry: do actors in your framework have a "Caller Queue" to send messages to or do you use the "Topic" for communication back to the Caller?

0 Kudos
Message 22 of 62
(2,134 Views)

Dmitry wrote:

    • A caller message may contain code that does something (malicious or not) else than calling Actor’s private method. When sent from a remote caller (Distributed AF) this opens a vulnerability on the local computer

The Programmer creates the messages, and the set of messages defines the actor.  If one has access to the source code one can extend the actor, maliciously or foolishly, with new messages, but if one can modify the source code you can do anything.  And one can't inject a new type of message remotely.  If one implemented some kind of remote-loaded message there would be a vulnerability, but this is the same security issue as any other kind of plug-in architecture.

0 Kudos
Message 23 of 62
(2,134 Views)

drjdpowell wrote:


I'm confused here; how does the API have the ability to send messages direct to subactors?  In other words, if the Actor creats a subactor, how does the address of the subactor get back to the actor's API? 

O, this one is easy – ArT_Actor API is part of an Actor ‘bundle’ (ArT_Actor Class, ArT_Actor Component Class, ArT_Actor API Class plus all ArT_Actor Message Classes). When ArT_Actor developer creates Actor API he has a chance to add subActor API objects to parent ArT_Actor API private data. And make any necessary calls to subActors after that. The important part is to keep an ArT_Actor API stateless – so that it can be freely branched/copied or distributed in any other way.

Ah, I confused Pausing with Locking.  But the Pausing seems to be getting more complicated.  How do I specify things, like safety monitering or PID-control loops, that must not be paused, even if a caller calls "pause" with a very high priority level?

Added later: Also, what about auxillery loops that aren't themselves actors?  Auxillery loops to do things like wait on a TCP connection for incoming data or read out a DAQ.

The default LVPQ (LabVIEW Priority Queue) Message Transport behavior is to put on hold all messages in Actor Message Loop at or below a specified priority level. If this is OK for the Actor under development – developer does not have to do anything else.

However, if your Composite Actor requires special rules for pausing Actor activities – you have to implement a concrete method in Composite Actor Class and call this method as a step in executing Pause API method. It should take care of all your auxiliary/PID/etc. loops and/or disallow pausing the Actor at or above some Priority Level. You may recall that Pause/Resume/Cancel calls send meta-messages (intercepted by Message Transport on the Target Actor side). At that point, Message Transport will execute private Actor API methods called in custom Pause/Resume/Cancel implementation within Composite Actor API).

In case an ArT_Actor developer needs to prevent callers from Pausing/Resuming/Canceling Messages, or make these functionality private – he has to make his Actor_API inherit directly from ArT_Actor_Component_API class instead of ArT_Message_Queue_API (remember the ‘Stacked API Design” on Page 11 ? BTW Figure 5 is now obsolete – I’ve already made some changes based on feedback in this thread).

Is the subscription handled by the framework, or does the User have to call "Subscription" on the Topic explicitly?

Correct. Caller has to call a Publisher Actor directly to subscribe/unsubscribe. If Publisher supports several Topics – one needs to call a Publisher API method for that specific Topic (i.e. one sub/unsub method for per Topic).

This is very lightweight and works well on static hierarchical systems. However it has a couple constraints:

·         Publisher is decoupled from callers, but callers are still coupled to Publisher Actor (need its API object to sub/unsub). This may present a challenge if you spawn a lot of Actors at runtime

·         For the same reason it is not as scalable as one may hope for - adding/removing Actors (even static ones) requires updating subscriber Actor code (pub/sub is a method call)

I still feel it is a good starting point – and added it to ArT_Actors - but it is not a part of ArT_Actors Core.

In case you have dynamic actors or need ultimate scalability – you can use a Message Broker (something similar to JMS (Java Messaging Service)). In this approach, both, subscriber and publisher are decoupled from each other by the Broker. The idea is quite simple:

·         Publisher registers it’s Topics with Broker under unique names

·         Subscriber subscribes to above Topics by calling the Broker with a specific Topic name

·         Subscription Lists are managed by Broker (Publisher Actors get leaner)

This has some obvious benefits:

·         Subscriber is coupled to Topic Name (may be kept in config file) and Broker API

·         You can have multiple publishers write to the same Topic

·         You can change publishers without making changes to subscriber code

·         Setting up subscribers becomes much easier – a publisher does not have to be running (yet) for an Actor to subscribe to Topics of interest. Messages would start flowing as soon as actual Publisher registers and starts publishing.

The latter is very helpful in case you have a distributed/dynamic actor configuration – it removes a lot of constraints on system bring-up – now you do not really care in which order to instantiate your Actors. I had a large (20+ QDSM Actors) application using a Message Broker – and never heard any complaints that using Message Broker increases application complexity.

I do not currently have a Message Broker implementation for ArT_Actors. But it should not be too hard to come up with one as in AF3.0 and ArT_Actors Messages are regular by-value classes...

0 Kudos
Message 24 of 62
(2,134 Views)

AristosQueue wrote:

Daklu and drjdpowell: Both of you said why it would be valuable, but neither answered my challenge that you can already do this today. My contention is that if you want this separation in any given actor, go ahead and give your message handling actor a private data member of another class -- any class. Why do you need an explicit "inside an actor" class hierarchy?

If the question is why does the framework need that hierarchy, it doesn't.  My comment was directed toward the general utility of the strategy as opposed to a reason to build it into the framework.

In fact I'd strongly argue against building it into the framework itself and let users implement it when it makes sense.  Far too many apis suffer from a kitchen sink mentality, with the api developer trying to add features for every conceivable situation.  The end result is an api with a complex list of options/features, which in turn makes it hard for new users to pick up.

As near as I can tell the primary focus of the AF is on safety.  You want to prevent users from shooting themselves in the foot as much as reasonably possible.  A message handler class hierarchy doesn't further that goal, so imo it shouldn't be part of the framework right now.  If the user community eventually settled on message handler classes as a best practice, it might be worthwhile to add them to the framework as a user convenience, but not right now.

@Dmitry

We've kind of picked apart your ideas like vultures on a carcass.  I want to clarify that I don't think your framework is bad.  On the contrary, if it helps you write better software faster it is almost certainly good.  It's more that it appears to be tailored to your coding style and business environment.  As such, it may have a hard time finding users who can benefit from it and are willing to take the time to learn it.

Frameworks have to walk a fine line between features and audience.  Every time a framework defines something it excludes potential customers from the framework.  For example, the AF defines dynamic launching as an inherent feature of actors, thereby excluding developers who don't want to use dynamic launching.  While your framework implements features that are valuable to you, my guess is they have limited value to the general Labview community.  (But it is just a guess.  Maybe people will flock to it and proclaim it the greatest thing since the transistor.)

-Dave

0 Kudos
Message 25 of 62
(2,134 Views)

drjdpowell wrote:

Dmitry wrote:

    • A caller message may contain code that does something (malicious or not) else than calling Actor’s private method. When sent from a remote caller (Distributed AF) this opens a vulnerability on the local computer

The Programmer creates the messages, and the set of messages defines the actor.  If one has access to the source code one can extend the actor, maliciously or foolishly, with new messages, but if one can modify the source code you can do anything. 

I do not trust my fellow developers. I also do not trust myself. I prefer G compiler or the Framework to enforce certain aspects in the code. That's why we have strict type checking

daklu nicely phrased a major Command Design Pattern feature - "I'll execute whatever you send me" . Thus my AF Actor will execute whatever custom payload somebody sends it. Quite often this is a desirable feature, but it 'spreads' (like electron in an atom) Actor functionality across the entire code base. There is no single location I can review to check everything related to my AF Actor behavior. I think having an option to enforce Message Encapsulation is quite important ... BTW, speaking on testability, this 'quantum' nature of Command Pattern Messaging will also increase load on V&V - and we all know to well that these guys are more often in critical path of the project than the development team ...

0 Kudos
Message 26 of 62
(2,133 Views)

Dmitry wrote:

I am curious if anyone knows a way to load a class/VI to a remote Application Instance via TCP/IP or Network Streams channel? Any such features on the embedded systems (i.e. LabVIEW RT) side ?

I do not know of any and I have been actively looking because remotely loading an actor that is *not* already present on the server is actually a substantial use case of interest when we start mapping the actor tree across networks. Distributed programming is all about "take this code and run it over on this machine".

The closest that you come is if we build a launcher harness where you send me the files for a class, I save them to disk and then I load them as a class in memory. There is no "load this class or VI from this buffer in memory" in LV 2012. There *was* something like that in 2009 through 2011, but it was a private function that has been removed because it was no longer needed by the toolkit that created it. That "something like it" wouldn't work for anything with hierarchy... just a single VI, so it would've been useless anyway for this project.

I've been banging on the security model for AF loading/running remotely quite a bit and asking questions of those who are security experts. I hope to have something nailed down in the next couple weeks as "this looks viable."

0 Kudos
Message 27 of 62
(2,132 Views)

Dmitry, though being concerned about network attacks is fine, your concerns about the network attacks coming from unintended messages are unwarranted. Yes, a Message's Do.vi can call any public method. Solution? Do not make any methods public that you do not intend to be called across the network. It is easy enough to create a shim actor that receives messages and then repackages them for a nested actor such that the nested actor can have 10000 public methods but only the three public methods on the outer actor are reachable from across the network. Design access scope correctly and this is not a concern.

As for "A caller message may contain code that does something (malicious or not) else than calling Actor’s private method. When sent from a remote caller (Distributed AF) this opens a vulnerability on the local computer" -- that's a general problem of remote loading code, not something specific to the actor framework. As long as we are operating with message classes that must already exist on the recevier, this problem doesn't arise. If we manage to get remote loading of classes working, then you have to have a secure communications channel, which, as I said in my previous post, is something I'm investigating.

0 Kudos
Message 28 of 62
(2,133 Views)

Now, a separate reply on the "separation of hierarchies" question.

I've read Dmitry's follow up post. I agree with the concern about "inheritance for two reasons". I even agree that his solution works and is sometimes useful technique. But it is not the *only* technique, and there's one that I would consider better that does not require any change of definition of the entities of the framework.

Yes, you can extend a class for UI, but that is not the only way to add UI. You can also have the UI running independently in a separate hierarchy (as is done in the 2012 evap cooler shipping example). Thus they become completely separate when they need to be... but both are actors. The actors themselves do not separate into a functional core and a transmission core. Rather the operation of the hardware and the UI to the hardware separates into two actors. In this setup you are free to add multiple data extensions to the hardware actor (as we do with the HAL layer in the example) and free to add multiple extensions to the UI hierarchy (the user-monitored UI and the free-running program UI). One nice functional benefit of this method of separation -- as opposed to the separation that continues to allow UI by inheritance -- is that the funcitonal actor can continue executing even if the UI shuts down, allowing for headless operation and/or different UIs being attached. Contrariwise, the same UI can keep going and change out which hardware actor is being messaged.

In other words, there's an alternate approach to separation of powers that does not involve splitting the core operation hierarchy. Encouraging function and transmission to separate in the general case would substantially add to the confusion of the AF for new users. Why do I say that? Because it is the sort of architecture that shows up in other messaging frameworks that have been presented to me over the years that make nearly everyone on the team go, "What the heck...?" The more entities in the system, the harder it is, but at least that goes up linear. The more *types* of entities in the system, the harder it is, but it goes up something worse than linear. At the moment, we have actors and messages. If you need a full separation of powers, it can be done, and done cleanly, without introducing a third entity to the system.

I do not think there is any need to separate the core logic from the actor-ness of the actors. Indeed, I think there are many reasons to keep them as tightly bound as possible. The actor, as an entity, receives messages and acts on them. That's its defintion. And it is foundational to the point that you can construct the entire system from nothing but such actors. It may be desirable in some apps to separate these, as Dmitry did in his app, but it is not something I would steer users toward as a first order use case of the framework.

0 Kudos
Message 29 of 62
(2,133 Views)

Dmitry wrote:

daklu nicely phrased a major Command Design Pattern feature - "I'll execute whatever you send me" . Thus my AF Actor will execute whatever custom payload somebody sends it. Quite often this is a desirable feature, but it 'spreads' (like electron in an atom) Actor functionality across the entire code base. There is no single location I can review to check everything related to my AF Actor behavior. I think having an option to enforce Message Encapsulation is quite important ... BTW, speaking on testability, this 'quantum' nature of Command Pattern Messaging will also increase load on V&V - and we all know to well that these guys are more often in critical path of the project than the development team ...

This is untrue. There is a single location to check for the correct functioning of your actor, and there is a second single location to check for the correct sending of the messages.

An actor will execute all the messages it receives. You need only check that actor to ensure that it is correct for any sequence of messages it receives (does something useful or returns an error if the message is meaningless). There is only one entity outside of the actor that is capable of sending messages to the actor: the caller. (For the purposes of this discussion, the nested actors are part of this actor since this actor knows what it is launching and, presumably, is not just launching random nested actors that it finds laying around in the system.) That means that the caller is what needs to be checked for correct messaging of the actor. And that is the only place that needs to be checked.

If the caller chooses -- emphasis on the word chooses -- to share the actor's queue with a third party, that third party now needs to be checked, but there is nothing compelling the caller to share that queue and no way for third-parties to get access to the queue otherwise. We don't have actors by name or any arbitrary listener/observer pattern built into the framework (unless you install the AF debugger, and even that I locked down so that you can't accidentally [or maliciously] try to hijack the queues... its only meant for monitoring).

The messaging system is essentially as locked down as the wires on a diagram -- only the upstream node can put data on a wire. Only the caller can enqueue messages. Only the downstream node can see the wire data. Only the actor can dequeue the messages. The downstream node executes with whatever data it is given -- either doing something useful or returning an error. The actor handles the messages by doing something useful or sending an error back to the caller. The entire Actor Framework is intended to be an asynchronous dataflow model, with all the "pipes are secure" aspects of dataflow. The fact that the pipe *can* be shared is the developer's choice, and when making that choice, the developer has to decide that they are going to increase their complexity in exchange for some other simplification.

0 Kudos
Message 30 of 62
(2,133 Views)