Jump to content

Daklu

Members
  • Posts

    1,824
  • Joined

  • Last visited

  • Days Won

    83

Posts posted by Daklu

  1. The only way to guarantee Labview loads the correct version into memory is to close and reopen the project.  Sometimes LV will notice the version on disk is different and ask if you want to revert (which in this context means 'load from disk,' not 'undo any changes you made') but I wouldn't bet on it noticing every time.

  2. All this talk of television shows and not a single mention of House of Cards?  Unacceptable!  (I keep thinking I ought to try the British version.)

     

    I'm finding I'll be interested in a televised drama for a while then get bored--mostly because nothing substantial is happening.  Revenge started out good but partway through the first season I realized the story wasn't going anywhere.  Arrow has a back story I find a little bit interesting, but again there's no progression.  I really enjoyed Weeds for a while--until it became too dark and I wearied of watching Nancy's self-destructive behavior.  Tried both Mad Men and Breaking Bad.  Neither struck a chord with me. 

     

    I'm a little more forgiving of sitcoms.  I enjoyed the early seasons of Big Bang, though I've never particularly liked Penny.  Been watching 30 Rock, and that's okay.  By far the best sitcom I've seen in the last 2 decades is Scrubs.  (And Zach Braff nailed an awesome photobomb the other day.)

     

    There aren't that many "good" web series.  I mostly enjoyed The Guild, probably because I was way into World of Warcraft for a while and could relate to the situations.  But it pales in comparison to the best web show I've seen:  Dr. Horrible's Sing Along Blog.  If you're willing to include Machinma in the web series category, Arby 'n' the Chief was pretty funny.  (If you're not familiar with it, start from the

    .  BTW, it's not safe for work.)
  3. Shaun,

     

    Yeah, Danny mentioned the Pi earlier.  Unfortunately, the Pi only has 8 GPIO pins.  This board needs 10.  Multiplexors or serial I/O options are possible, but this is a project for high school students in their second year of programming who will have limited help and supervision.  The environment must be simple enough to set up for them to make progress quickly.

     

     

     

    Jordan,

     

    I see two different ways to approach using the Uno.  The first is to program the microcontroller using c/c++ to control the lights on the circuit board directly.  The second is to write firmware and have the student use the Uno as a usb digital I/O module, which is what Darin was suggesting.  What kind of experience have you had using the Uno as a standard usb device?  Have you had any troubles getting it to work well?

     

    (As an aside, since the Uno comes with a usb port it potentially sidesteps the problems others were having with the FTDI chips.)

  4. I bought the OEM version... $75.  Yeah, that's still a lot of Arduinos.  I might pursue firmata on my own, but I'm very concerned there will be too many difficulties getting in the way of the student's success.  In particular, it appears they'd have to use a USB serial port and there looks to be some issues with that.  Not a big deal for an engineer--potentially a project killer for a high school CS student working independently.

     

    (What arduino would you recommend for a simple digital I/O module?)

  5. Thanks for the leads.  I ended up purchasing a LabJack U3.  They don't have a Java API for that model, but it does support Python, and they are introduced to Python at the end of the first year.

     

    The firmata library looks very interesting.  Unfortunately there are just too many unknowns for me to be confident I could give them something soon enough.  (Namely I have no practical experience with Java, Arduino, or fermata.)

  6. My daughter is taking a high school computer science class and learning Java.  I met with her teacher last night as part of a "technical advisory board," and she asked for some projects her second year students could work on independently.  (Second year CS students attend the same class as the first year students, but for the most part they choose their own projects and work on them independently.)

     

    Being a hardware guy, naturally I think programming is more fun when there are flashing lights and moving parts.  A couple years ago I purchased a "traffic light kit" that is kind of neat and dirt cheap.  However, writing a desktop app for it requires a digital I/O module with a Java API.  Unfortunately there doesn't seem to be much Java support in the hardware community.

     

    Does anyone know of a low cost USB digital I/O module that comes with a Java API?

  7. But what about external Actors wanting to have the sequencing actor perform just step 5?  But what about external Actors wanting to have the sequencing actor perform just step 5?

     

    External actors shouldn't know anything about individual steps in the process.  All they know is they send a StartProcess message to your SequenceController actor and get a message back when the process is done.

     

     

    A concrete example I am thinking of is reading a settings file.  You need to read this file as part of the process when you init your application and configure your system.  But what if the customer requested the ability to re-read the file while the application is running because they edited it with an outside tool and want the new settings to be applied?  If that part of your initialization process was a separate message, you could execute just that part again.

     

    Your question is more about API design that AOD.  I can tell you one way to do it; I can't tell you what you should do.

     

    I usually implement configuration settings as ordinary classes, not as actors.  A typical AppCfg object for me might have the following methods:

     

    -Create AppCfg (path Path);

    -Save ();

    -Get configItemA();

    -Get configItemB();

    -Set configItemB(string Key, string Value);

    -etc.

     

    During initialization the startup code creates an AppCfg object, which load the data from disk, and hands the object off to whoever is responsible for that data.  If there is a need to reload the AppCfg data while the app is running, the actor responsible for that object can expose a ReloadCfgData message.  When the message is received it would discard the existing object and create a new one from the file on disk.  There's no reason for the initialization code to expose any steps in the process, because the initialization code is only run during startup.

     

    I try to write my component APIs so each method is independent, meaning there's no overlap in the methods' functionality.  If the Initialize method calls LoadAppCfg, I wouldn't expose a separate LoadAppCfg method.  I find the component is easier to use and the code is easier to understand when I don't have to keep referring back to the documentation to remind myself exactly what low level API methods are called by each high level API method.

     

    That said, there's nothing in AOD that prevents you from exposing low level and high level messages in the same actor.

     

     

    This precisely why an EXAMPLE would help.  Presumably IF there were general rules THEN you could just specify those and pointe finale.  :yes:

     

    Examples don't help with figuring out when an actor should be used.  An example can show how to implement them, but there are way too many variables for me to tell someone when they should use an actor.  When do I use actors?  Any time I have multiple processes that need to run concurrently.  When should you use actors?  When the business conditions are suitable to do so.

     

    (I am working on reimplementing the Evaporative Cooler example, but my motherboard gave up the ghost over the weekend so I have to postpone it for a while.)

  8. So, if the MHL does not trigger the next step in the sequence by sending itself a message, are you saying that the 'helper loop' should send the MHL a message to do the next step in the sequence?

     

    No, I'm saying the MHL sends a message to the helper loop telling it what step to execute.

     

    MHL:  Do step 1. Here are the data you need.

    HL:  I finished step 1.  Here are the results.

    MHL:  <checks results and figures out the next step>  Do step 2. Here are the data you need.

    HL:  I finished step 2.  Here are the results.

    MHL:  <checks results and figures out the next step>  Do step 8. Here are the data you need.

    etc.

     

    In the presentation I said any time I drop a MHL I consider it another actor.  That's another simplification.  I only call a MHL an actor if it has the characteristics I consider essential for agile actors--atomic messages (no sequential dependencies) and always waiting.  The helper loop in this case could be a message handling loop, but it's not an agile actor because it directly executes lengthy processes.

     

     

    You are making me sorry I used a database as an example.

     

    Really? I think it's a remarkably good example.

     

     

    You don't actually have to do that as you can execute two transactions on the same connection at the same time (the database will sort it out). 

     

    Depends on the database.  Some only process a single transaction at a time on each connection.  That's why I asked which one you are using.  :P

     

     

    This is repeated many more times, resulting in a bunch of connections being added to the pool just so they can fail to connect, the error log is so convoluted with messages that it because nearing impossible to untangle the threads of errors, and all for what?  So you can call two database transactions at the same time?  Not worth it.

     

    There's nothing preventing the connection manager from monitoring the database and refusing to hand out connection objects if it is down.  Anyway, I'm not trying to tell you how to design your app.  I'm just trying to understand why you are favoring imposing synchronous access on an inherently asynchronous process.  If the tradeoff for having a clean error log is worth it to you, I'm certainly not in a position to tell you it isn't.

     

     

    The point I was trying to illustrate was an example where something other than an actor was the best solution.  If you don't like my example, think of a different one.

     

    I'll grant you your solution is a viable alternative, and may even be the best solution in your situation.  I'm not anywhere close to being convinced it is "the best solution" in general.

     

     

    I just think your presentation would benefit from a discussion of where it is appropriate and not appropriate to use actors.

     

    I understand the sentiment, but there are no general rules for when they are and are not appropriate.  It's appropriate if you can build it so it fits into your design and behaves how you need it to behave.  It's like asking when it is appropriate and is not appropriate to use classes.  There are no technical reasons not to use classes, and there are no technical reasons not to use actors. 

  9. So, to summarize If you send a message to the actor telling it to do something that takes time, it should hand that off to a helper loop and go back to listening for more messages.  Lengthy processes should never be done by the message handler.

     

    Correct.

     

     

    As for sequencing, I think the actor should encapsulate the sequence from the caller but I am less clear on why it is bad to call itself.

     

    I don't like having *loops* send messages to themselves.  An actor may consist of multiple loops, so an actor can send messages to itself (helper loops) internally.

     

    Having a loop send messages to itself isn't inherently bad--it is possible to do it safely.  I choose not to do it because it is very easy to overlook race conditions and very hard to verify they do not exist.

     

     

    By making the database object a singleton, you can reuse a connection between calls (saving the open-close overhead).  This makes most sense if you anticipate making a lot of calls but not a lot of simultaneous calls.

     

    What DB are you using?  Databases support multiple connections.  I don't understand why you are intent on limiting yourself to only one connection.

  10. The thing that strikes me about that picture is, first, the fact that the queue has two writing processes, and second, that subactions of handling a message are treated as new messages for the queue.  I agree with you about the problems of this, but don’t think that this is best communicated by arguing about how to define the differences between two very similar acronyms. 

     

    The purpose of defining the differences between QSM, QMH, and MHL is to try and make them meaningful.  As long as there is no agreement on definitions they have limited value in communicating ideas.  In forum language the three terms are used interchangeably, causing ambiguity and confusion.

     

    I don't have strong objections to Shaun's definition of MHL:  A message handling loop handles messages.  That definition has value as an abstract idea.  When a MHL is identified it tells me something useful about its role in the application.

     

    For the time being I'll refer to actor-style message handling loops as "Safe" MHLs (SMHL), meaning it can receive any message at any time without breaking the contract of its messages.  Any MHL may be safe or it may be unsafe.  The only way you can tell is by inspecting the code.

     

    Calling the subset of message handling loops that use a FIFO to buffer messages a QMH may be how the term is commonly used.  If so, that's fine with me--I can adapt.  However, I don't think it's a useful distinction in the same way identifying a LIFO MHL, or SEQ MHL, or Random Access MHL, or Notifier MHL isn't particularly useful.  It gives me information about how the MHL is implemented, but from an actor-oriented viewpoint I don't care how it is implemented.  All I care about is that it is safe.

     

     

    I think it would be better to identify and talk more directly about the issues of message ordering and perhaps introduce more illuminating terminology, for example the concept of a “critical section”, or the database-transaction concepts of “consistency” and “isolation”.  The “Initialize” transaction is a critical section, and because it isn’t protected as such it isn’t isolated from other transactions.  Adding a different (non-FIFO) transport, that may switch the order of the two subactions, would also prevent it being consistent.  

     

    I'm not opposed to introducing new terminology, and I sometimes view sequences of messages as transactions.  But since you introduced the terms you get to explain what they mean in the context of a MHL.  :P  I believe these terms have value as abstract concepts.  The more difficult question is how does one identify critical sections in a MHL?

  11. It seems to be that in some cases, when a message is executed, the last step of the execution may be to execute another message (send to self) to continue some sort of sequence.

     

    Oh, you mean like QMH/QSMs?  :P

     

    Having a loop send messages to itself is a dangerous game.  It's not well-understood exactly when it is safe to do so and when you are introducing race conditions.  As far as I can tell, the only way you can guarantee race conditions don't exist is by not maintaining any loop data in a shift register.  Once a loop becomes stateful race conditions have to be rooted out by inspection.  That can be very difficult.  You have to make sure the implied contract of each message will be fulfilled regardless of what other messages arrive before the sequence is complete.

     

    Example: There is a race condition in the QSM project template that ships with 2012.  In the Initialize message handler, it is possible for a ChangeData message to be sent after InitializeData but before InitializePanel.  If that occurs, then the Initialize message isn't honoring its implied contract, because the panel isn't in its initial condition when the sequence completes.  You can't easily tell the race condition exists--you have to trace through each message and figure out what they do.

     

    Personally, I prefer to avoid that issue altogether by not self-sending messages.  It makes it much easier to verify the app behaves correctly.

     

     

    I am really leaning towards the DVR idea.

     

    I agree with James.  It sounds like you're over-constraining your application.  Why is it an advantage to only allow one actor to access the DB to the exclusion of all other actors?  You're imposing synchronous behavior where it isn't required.  It makes more sense to me to have a ConnectionPool actor that sends connection objects to individual actors that need DB access.  The actor can use the connection to do its sequential operation.  When it's done with the connection it can return the connection object to the ConnectionPool actor.  No DVR needed.

     

    (This does expose you to the possibility of a connection leak if an actor does not return the connection when finished and asks for a new one the next time.  You'd have to verify correct behavior via unit tests or inspection.)

     

     

    What good is a MHL that is always waiting if it is waiting for a child actor to shut down?  It seems that it is still being blocked in that case.  I just do not see a way to truly free up all actors at all times when there are processes in an application that take unknown amounts of time to execute.

     

    The point of having a waiting MHL is that it always has the most up-to-date information available to make decisions.  Information is never stuck in the queue waiting to be read.  Actually, "always waiting" is a simplification.  What you want to avoid is having more than one message waiting to be read.  Whenever a second message is pending, it is possible the message contains information that alters how the loop would have processed the first message.

     

    Always waiting does not mean the actor will be able to instantly do what the message requested.  If the database API doesn't provide a way to abort a query in progress, then there's nothing you can do to change that.  You have to wait for it to finish or timeout.

     

     

    But I just can't seem to wrap my head around how to do this for applications with a lot of sequenced steps.

     

    It's just a matter of choosing the correct encapsulations and defining the desired behavior.  Actors only expose messages which are guaranteed to fulfill their contract regardless of what other messages arrive in the meantime.  Like I said earlier, if I had a sequence of steps that needed to be executed I'd wrap the sequence in an actor expose a few high level messages like StartSequence and AbortSequence.  All the sequencing logic would be hidden inside the actor.

  12. I disagree, if you mean you can update these lists of actors non-locally.  For example, if Actor A launches Actor B, then only A has the address of B, and A cannot add B to any list of Actors held by any third actor, C, except via sending the address in a message.  Having by-reference updatable lists shared between Actors is a definite violation of the Actor Model; the lists have to be by-value.  

     

    Actors don't maintain lists of other actors; they maintain lists of addresses.  Each address is a list of zero or more actors (and zero or more other addresses) who will receive messages sent to that address.  I agree an actor's internal list of addresses is localized; however, the address itself must be implemented as a reference or global for it to have any use in the AM. 

     

    Reading back over this discussion, I think your use of the term "global addresses" is too vague.  You may be referring to addresses in an unmanaged (meaning no actor is responsible for it) global data store.  In that case I agree, since as near as I can tell the AM doesn't allow anything to exist that is not contained in an actor, address, or message.  I'm referring to the address' behavior when implemented.

  13. Perhaps I was not being clear.  What I meant was to design the system so the Actor that had the ability to tell an actor to do something with data was the one that also sent the data needed.  So, in theory, you would not get in a situation where an actor get a message it could not act on because the message was designed to include the data in the first place.

     

    I see what you mean.  The receiving actor has a single behavioral state, so all messages are always handled exactly the same way.  Yes, you can do that for certain actors.  I don't know if it is feasible to design all your actors that way.  My gut feeling is eventually an actor somewhere in the app will need to handle a message differently depending on its internal state data.

     

     

    Actually, I think (no global addresses) is a property of the Actor Model (though descriptions of it are not very clear so I may be wrong)...

     

    Here's my reasoning for why I think it is not.

     

    In the Actor Model (AM) actors don't send messages to other actors; they send them to addresses.  An address is an abstraction of the list of all actors who will receive messages sent to that address--an alias of sorts.  Nothing I've seen in the AM indicates the list of actors the address refers to must be static.  Actors can be added to or removed from the list during run time, but it's not necessary to send messages to every actor that currently has the address to let them know the list has changed.  The change is handled automatically, sometime behind the scenes (such as registering for user events) and sometimes in code (such as a subscription manager actor.)

     

    Saying named queues aren't allowed in the actor model oversimplifies things.  I could use a named queue and pass around the name instead of the reference itself, and that wouldn't violate the AM.  The important thing is the actor doesn't know about it before it's supposed to know about it.  If your Planet Killer missile system is designed so every actor needs to always know the address of the Self-Destruct subsystem, then I don't see anything in the AM that says you can't use a global address implemented in whatever way you want.

     

     

    Would you dynamically create an actor to handle each DB request and have it destroy itself after it completed and returned the results?

    What if you wanted to share a DB connection, to avoid the expense of opening and closing the connection for each transaction?  You could wrap the DB class in a DVR and then have each DB actor use that object.  This would have the effect of serializing your DB calls (if you needed logic to do error retries and restore the connection if it goes bad).  Would that be a bad implementation?  (this could apply to any shared resource like a file or some hardware)

     

    It's been a long time since I've done any programming with DBs, so my terminology might be wrong and certainly some of the details on how I think they work are wrong.  For this response I'll use the term "DB connection" to mean a connection to the database that can only process one operation at a time.

     

    The general solution to reuse anything expensive to create is pooling.  The initial approach I'd take with a DB is to create an actor that abstracts the database.  The DbActor creates a private pool of connections it uses to service the requests it receives.  Internally it keeps a look up table of the connections currently executing an operation and an address where the response should be sent.  When a connection returns a response DbActor looks up the associated address, sends the response to it, and returns the connection to the pool.

     

    I would not distribute DbActor in a DVR for the reasons you mentioned.  I also would not give connection objects to the other actors.  One of the responsibilities of DbActor is to manage the connection pool, and if it is giving connections to others it can no longer do that.

     

     

    It seems to me that it might be best implemented as an actor or helper that has limited lifespan and is exclusive the caller.  But, my only concern is cleanly shutting down the system if we try to exit while the DB actor/helper is in the middle of processing.  If we are going to call it an actor, then it is already violating the principle that its MHL is always waiting, right?

     

    Helper loops are always exclusive to a single actor.  Same for sub actors in hierarchical messaging.  That exclusivity makes certain things much easier to reason about, but adds the overhead of having to write extra message routing logic.

     

    Regarding shutdown, when I have low level processes that might take some time to shut down, I write my shutdown logic so an actor doesn't shut down until all its sub actors have shut down.  That's easy enough to do with hierarchical messaging.  Aren't you using a variation of direct messaging?  I've never tried doing a controlled shutdown with that, though I suppose you could as long as your actor topology has a clear hierarchy of responsibility.

     

    Yes, in my Agile Actor model the MHL is "always" waiting.  If the MHL allows messages to remain on the queue for "too long," then in the AA model it is not an actor, because it requires priority messages or transport manipulation to get the behavior you want.  (The AM makes no guarantees about the arrival order of messages, so priority messages can not exist.)  I'm not familiar with NI's database palette.  If the function for querying a DB blocks, then that function needs to be in a helper loop, not actor's MHL.

  14. I'll respond to the rest as I get time, but this part jumped out at me.

     

    I want to design it such that an Actor is never asked to do something it is not ready to do.

     

    NO!  This is the wrong approach to take! 

     

    You must accept that you cannot--in general--prevent an actor from receiving a message it is not prepared to act on.  Even if you only have two actors talking to each other.  This is a fundamental truth of concurrent programming.  Quit trying to break the laws of time; it's not likely to work out in your favor.

     

    (I've written about this subject before.  Search for "sender side filtering" or "receiver side filtering" and you might find something.)

  15. The cooler example is too involved to quickly understand - the theory would get lost under the wires (unless one already knows AF, in which case simple code can teach the theory just as well as more complex code).

     

    I've been poking around in the cooler example again.  IMO, a big reason it is so complicated is because it illustrates so many different concepts and has a lot of abstract classes.  I've started an AA implementation that strips away all the extra stuff and focuses on the basic AOD principles.  I'll post it in a new thread when it's ready.

    • Like 1
  16. Maybe one way to go is to show a simple implementation of a particular concept in each AOP style: AF, AA, QSMLH (queued-state message-loop handler).

     

    I can do the AA implementation, but I don't want any part of a QSMLH implementation.  Maybe someone from NI would do it? :shifty: (Don't forget the JKI-SM.)

     

     

    You state in your presentation that the message handler should always be waiting. Therefore, it needs to hand off action to another actor or a helper loop ASAP when it receives a message. This sounds a lot like the producer part (UI Event loop, for example) of a standard producer-consumer architecture. The goal is to always be ready for the next message. In command pattern message systems like AF, what do you do if the message says to do something that takes an unknown amount of time? For example, what if the message requires the actor to access a database? That database call can take significant amount of time depending on what the call is doing and if there are retry-able errors. Should that be handed off to a helper loop? Or should it be handed off to another actor?

     

    Yes, that task should be handed off to either a helper loop or a sub actor.  Which one you choose depends on how much functionality that logical thread needs.  If it’s just waiting for a response from the db, will forward it to your MHL, then exit, I’d say make it a helper loop.  If instead it’s an abstraction of the database connection, I’d probably make it a full-blown actor.

     

     

    If you say helper loop, then do you create a helper loop for every potentially slow action that acting on a message can instantiate? That seems like a lot of loops that could be hard to maintain.

     

    I create a helper loop or sub actor for every computation that is potentially slower than it needs to be to meet the project’s requirements.  Is it a lot of loops?  *shrug*  I dunno… what do you consider a lot?  I probably use more loops than most other developers.  Taking a quick look through a smallish RT project I’m working on, the RT code has 17 while loops and the FPGA code has 13 loops.  Is it hard to maintain?  Not if you do a good job assigning responsibilities and designing the API for each loop.  (Incidentally, helper loops can still be dynamically launched, so you don’t need to create 100 static loops on your block diagram just in case you’re waiting for 100 db connections.)

     

     

    If you say, create a DB handler actor, then what is there to stop that actor from backing up with requests for database calls if the database is running slow? (a very common real-world occurrence)

     

    You are correct—it’s turtles all the way down.  That’s one of the reasons why I make a stink about the difference between a MHL and QMH.  Eventually the actual waiting part will be delegated to a helper loop.  How many layers of actors you need to go through to get to the helper loop depends on your design.

     

     

    My next challenge is dealing with state. Actors in AF (and in other actor based implementations) often have state data associated with them. This can be set at initialization or can be changed by a message at some point in the life of the actor. This state data can also affect how an actor responses to message. In the example above with the database, let’s say an actor is sent a message that says 'do this action' but that action requires information contained in a database. The actor needs to get the data before it can fully process the message. How do we best handle this?

    Do we do the DB call in-line and wait for the data, then take the requested action? That would block the actor. Do we hand the DB call off to the helper loop or DB handler actor and have it populate the state data with a later message that then also triggers the action?

     

    Most of the time I design systems without having to use request-response messaging, but sometimes it is necessary.  When I need a RR message I usually design them to be non-synchronous.  Actor A sends the request to Actor B, optionally setting an internal flag indicating “I’m waiting for a specific response.”  When a message arrives from Actor B, A checks the flag to see if it's the message he is waiting for and acts accordingly.

     

     

    What if the actor receives another message that depends on that state data from the database but the helper loop/DB actor has not returned it yet? Do we save that message for later? Do we ignore it? Seems complicated.

     

    I can't tell you what you should do--what behavior you want that actor to have?  You could discard the message and tell the sender to try again later.  You could store the message in an internal buffer and process it when the first DB call returns.  You can open another database connection (dynamic launching FTW) and have both queries going in parallel.

     

    [Edit - If you don't know what you should do with the message then your system design is incomplete.  Step away from the code and go back to your model.  That's where you'll find the answer to your question.]

     

     

    The last challenge is how to get state machine behavior out of an actor. If you have a series of steps that need to happen and are set off by a specific message (like 'run this script'), how to you construct a set of actors and helper loops to accomplish that? You want your actors to not be blocked. You want to be able to interrupt the process at any point with an outside message. You want to be able react to state changes between messages and change the order of the steps (say, the script has a 'goto' or an abort or pause step in it). So, you do not want to queue up a bunch of messages like your favorite QMH. But you do need to have the system react to changes and take appropriate actions. Does all of this belong in a single actor with helper loops or a set of actors or something else entirely?

     

    You’re asking about a sequencer.  Like implementing a flow chart with branching logic and whatnot, except you also want interrupts (which incidentally flow charts don’t allow) right?  I’ll describe the basic approach I take to ensure there are no race conditions, but there could be alternative rule sets that are also thread safe. 

     

    Let’s say you have designed your process using a flow chart and decided it is correct.  Create a sub actor with a message handlers for each process block on your flow chart.  Do not create message handlers for decision blocks.  Do not have message handlers queue up other message handlers.  The sub actor is simply a concurrent thread capable of executing the individual processes required by your flow chart.  It knows about each step in the process, but it has no idea how the steps are connected.

     

    Every time a step is finished, the sub actor sends a StepCompleted message to the super actor along with any data that needs to be persisted or is required for branching logic.  The super actor takes that information, figures out what the next step should be, and sends a message and data to the sub actor requesting it to do that step.  Rinse and repeat until your process is complete.

     

    The super actor is responsible for knowing what the next step should be; the sub actor is responsible for knowing how to do that step.  Because the super actor’s queue is not clogged up with lengthy flow chart steps, it is free to receive messages from external entities requesting to change the normal sequence of steps.  That’s about as close as you can get to implementing interrupts in a data flow language.

     

     

    Boiling this down to some fundamental questions:

    How do you deal with data dependencies between messages (state)?

    How do you decide when to in-line, when to make a helper loop and when to create another actor?

    How do you implement state machine behavior in an actor based architecture?

     

    1. I don't understand what you mean by "data dependencies between messages."  Can you give me a use case to help me understand?

    2. Any message handling code can be inlined as long as it finishes before the next message arrives.  (Helpful, huh?)  As a rule of thumb, I inline data accessors and decision logic (the brainy stuff) in the MHL.  Lengthy computations, continuous processes, periodic processes, etc. (the brawny stuff) gets pushed into helper loops and sub actors.  Deciding between a helper loop or sub actor comes down to how much functionality you want that logical thread to have.  Complex functionality requires an actor, simple functionality gets by with a helper loop.

    3. I assume you mean behavioral state machine, and not queued state machine, yes?  I implement BSMs by nesting message handling loops inside a simple state machine.  (If you really meant queued state machine, then my response is "I don't."  They behave badly, why would I want to replicate that?  :P )

  17. How about creating examples of how to use Actors to solve common programming challenges or implement different architectures without using LV code?

     

    The metronome and job processor examples were my attempt to show solutions to common challenges.  Granted, they are LV code instead of abstract animations... (What can I say, it takes a long time to create complex animations in powerpoint.)

     

    As far as common programming challenges, integrating continuous processes, periodic processes, and/or sequencers into an application are the three things that come to my mind.  What other kinds of challenges would you like to see implemented?  (I don't think there's any point in showing how to implement different architectures.  Once you understand how to integrate concurrent processes safely implementing an architecture isn't difficult.)

     

    I've been considering reimplementing the AF swamp cooler example using my style of AOP (which I self-righteously dub, "Agile Actors.")  Would that be helpful?  I've mentioned many times writing takes me a looooong time.  If I write the code would someone else be willing to take the lead on doing a write up for it?

    • Like 1
  18. Thanks for the feedback Jim.  Code samples are a tricky problem I haven't figured out how to address yet.  It's not like any of the implementation is particularly complex or elegant.  It's all just very straightforward LV code, minus those implementation practices that lead to trouble.

     

    To everyone who has suggested sample code would be helpful, assuming there was sample code available...

     

    1. Which best describes your reason for looking at the sample code?

    a. To better understand the theory.

    b. To figure out how to implement the actor model.

     

    2. What are the top two things you understand least and want to examine in the sample code?

    a. Actors in general

    b. Transports in general

    c. Messages in general

    d. Message handling loops

    e. Helper loops

    f.  Interactions between loops

    g. Other? (Specify)

     

    3. What other information or knowledge would you hope to gain, or questions would you hope to answer, by examining sample code?

×
×
  • Create New...

Important Information

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