JKISoftware / JKI-State-Machine-Objects

Object-oriented framework for LabVIEW based on the JKI State Machine
BSD 3-Clause "New" or "Revised" License
96 stars 55 forks source link

[Discussion] Public Methods, Addressed Events and Wait Reply #19

Closed 3esmit closed 8 years ago

3esmit commented 8 years ago

The common way to communicate with SMO is by exchanging events, but sometimes many SMOs would register and send the same message, so there would be many ways to get around this, even by adding a message indentifier or a wait reply using DVR + occurences. Occurences would be vulnerable to race conditions... Example: Consider "SMO.Mongo.lvclass", a SMO that insert & loads variants from MongoDB. I want this class to be extensible, so normally projects with complex queries would inherit it. All SMO that DB access would composite SMO.Mongo.lvclass, like UI.UserManager.lvclass would call API "Put Document.vi" normally, but "Load Document by ID.vi" would need a reply. I could easily check if an incoming event with document _id reply at "UI.UserManager", but at some cases when a list of documents is asked there is no parameter for verification, so I would need to add a field in "List Documents.vi" api call like "call-key" and send this in replies, so I could get an addressed event, all event structures waiting that case would need to check if the key matches, but I don't like this solution as it seems a lot of overload. Another idea I am thinking is to make this a byRef SMO, and after API call wait on occurence, so when SMO.Mongo loads response in a proper DVR it fires the occurence, so API call can read DVR with response. Of course that occurences are not safe, but then a 'call key' could be added, and if this is the case, why not create a queue, message the SMO with that queue and wait for element then destory? Or why not using then Notifier?

I don't know what is the best option and why I should use one over the other option. This SMO.Mongo.lvclass is a real project I'm planning to write in order to stop worring with database structure. Currently I create 2 projects with a SMO DBs (EnduranceDB.lvclass & DynaDB.lvclass) using Mongo.lvclass (source avaliable in my repo) and make all project logic there, but then for each project I will need to change my reusable subsystems, like UI.UserManager.lvclass. So I want to create a SMO.Mongo.lvclass and make layers of specialization, so I could make Mongo.Endurance.lvclass, Endurance project have user authentication, so I just add UI.UserManager.lvclass and it already works with my Mongo.Endurance.lvclass as it is a SMO.Mongo.lvclass. Currently I need different source for UI.UserManager in 2 projects, as the SMO DB is different.

francois-normandin commented 8 years ago

You can imagine there is no single answer to this question. :-)

An important thing to consider when you decide on a method for your queries is whether the calls need to be synchronous or asynchronous.

For synchronous calls, I'll generally rely on private events (from within public method) to send commands to the Process and wait for a response using a notifier. The public method creates a notifier and passes it as part of the private event payload. The process will handle all commands in sequence and be responsible for any modification of the class private data, ensuring that no race conditions occur. The process replies using the notifier. (If I'm not mistaken, there is a dictionary example in the HAL Webinar code that relies on that mechanism. [apply vipc file and it should be in SMO palette])

drjdpowell commented 8 years ago

How does one do asynchronous replies in SMO? As someone who has not studied this package, the OP is a rather worryingly long bit of technobabble, given that asynchronous replies are conceptually very simple (Me: “Request 123: what is X?, You: “Re: Request 123: what is X?, X=10”).

francois-normandin commented 8 years ago

Hello James,

Here's a snapshot of the code I was referring to. It checks if the SMO is started (has a process). If it is, it signals the process through PrivateEvents to use a notifier to send the reply. API call waits for notification to come back. API is blocking for duration of call, process is processing asynchronously the requests. If the process it not running (SMO created, not started), it simply returns the byRef value stored in the private data. More instantaneous and non-blocking, but possibly stale value.

image

My response was perhaps too vague because one could use any type of callback (wink: Messenger Queue). SMOs don't enforce any standard pattern for those queries. I think at least one of these standard mechanisms should be scripted in the SMO Editor, but I haven't personally gotten around to do that just yet.

drjdpowell commented 8 years ago

Ah, but that’s a synchronous reply. Synchronous from the point of view of the caller/requestor, who is blocked waiting for the response. How does one receive the reply asynchronously, without blocking? I’m imagining passing a private event in the message, along with some kind of identifying information.

BTW, in you code example, why are you not destroying the reply Notifier after use?

francois-normandin commented 8 years ago

You should implement your API call with a non-blocking event or messaging queue if you choose so. If I want to receive a reply later, in an async way, I generally rely on a Public Event I'm registered to. But I think I see what you mean: is there a generic way to request a callback when the data is available? At this time, there is none. Not in the base class anyway.

As for the notifier... you are right that the API is responsible for the reference and should destroy it. I don't know why this one doesn't have it, it is part of my template. I'll have to review this piece of code for similar leaks ;-) I also explicitly put a note in my processes to not destroy the notifier ref as the API is responsible for that. Process should ignore error 1 if it occurs on sending notification (as in: API has stopped waiting for it). Thanks for pointing that out!

drjdpowell commented 8 years ago

Minor suggestion: use a one-element Queue in place of the Notifier, as reading a Notifier involves a copy of the data.

francois-normandin commented 8 years ago

I'll close this thread and refer to issue #26 .