LabVIEW Idea Exchange

cancel
Showing results for 
Search instead for 
Did you mean: 
crabstew

Promises as a datatype in LabVIEW

Status: New

This is something that started as a way to get data back from Actors in non-actor code (for example, web services). I've never cared for the blocking nature of Reply Msgs and the only other built-in option for getting back data is to make everything an Actor, which is not always an option. Promises solve both of those issues.

 

The basic idea is an enforced single-writer, many-reader cross thread datatype. In the current implementation, they are not much more than a locked-down, single element queue but you can actually do some pretty cool stuff with just that.

 

In the message sender, we create the promise and return it. The idea here is that the Actor owns the promise and will fulfill it. This gives us very low coupling for free.

Actor_Example_lvlib_Start_Long_Running_Process_Msg_lvclass_Send_Start_Long_Running_Processd.png
Wait on Promise will wait for the Promise to be fulfilled. It is a malleable VI so that a Default Value (for timeout) and Type can be wired. Using the timeout lets us do other things while waiting for data.

Actor_Example_lvlib_Launcherd.png
Inside the Actor, we can set the value with Fulfill Promise. Remember, once the Promise is set, that's it, no changing it again. In fact, Fulfill Promise will error out if called twice on the same promise.

Fulfill Promise.PNG
Something else really cool we can do is fulfill a promise with another promise. This may seem pedantic at first but it can help keep your code cohesive by passing the responsibility of fulfilling a Promise to another process. For example, if you have a message broker that just forwards a message, you can have the message broker fulfill it's promise to the caller with a promise from the callee.

Fulfill with Promise.PNG
We can also reject a promise with an error message that will be returned by any Wait on Promise that tries to read it. Rejecting a promise does fulfill the promise so you still cannot set the value later and you cannot reject a promise again.

Reject Promise.PNG
Finally, we have Destroy Promise. It does exactly what it says on the tin.

 Destroy Promise.PNG


I'd love to hit some more of the design points from https://promisesaplus.com/ (mainly adding Then callbacks) but I figure it's at a pretty good spot to share and get some feedback. I'd also like to try to figure out network communication at some point but that's probably a ways away (without some help anyway Smiley Wink).

 

I'm hosting the code at https://github.com/kgullion/LabVIEW-Promise if you are interested in checking it out or contributing!

8 Comments
drjdpowell
Trusted Enthusiast

I use the similar "Future" terminology instead of "Promises" in my "Messenger Library", but also build them with single-write queues.  These grew out of this conversation on LAVA.

crabstew
Member

Thanks for the link, it's surprisingly hard to search for "Futures" and "Promises" in relation to LabVIEW! Found a bunch of marketing stuff though...

 

There is definitely some great discussion in the first couple of pages of that post. Had a few things "click" after reading through it (and a good nights sleep). I actually decided to split the implementation into two classes, a "writer" class (the Promise) and a "reader" class (the Future). Similar to the way it's explained here: https://stackoverflow.com/a/27793863/992385

 

I also added an initial implementation of async callbacks. I haven't needed anything quite like that in my projects but I could see it being a nice solution to the polling issues AristosQueue brings up in the linked thread above.

 

I did not follow the A+ spec exactly (don't think that's currently possible in LabVIEW) but instead tried to do LabVIEWy things instead. For example, instead of having two callback inputs on Then, there is just one and the error case structure can be used to tell the difference. Definitely looking for some feedback on the API there.

 

Glad to see I'm not the only one who wanted something like Promises and Futures in LabVIEW!

AristosQueue (NI)
NI Employee (retired)

This is an area of great interest to me. I'll add my kudos.

crabstew
Member

I've been on a bit of a spree with breaking and fixing the code. Commit 98c26de (zip) seems to be working pretty well if you want something to poke around in. The only thing I currently know of that's not working in that commit is callbacks on a future that was fulfilled with another future.

 

I've also updated the API a bit, this shows most of the changes pretty well:

Capture.PNG

I also changed the wires appearance. The Future wire is supposed to look like it's nougaty center is protected (ie read-only) while the Promises nougaty center is exposed (read-writable). ¯\_(ツ)_/¯

crabstew
Member

The latest version of the code seems to be working pretty well, I would like to add some unit tests at some point but it seems like the callbacks are working correctly now.

 

I did discovered some odd behavior where if I create a Promise outside an Actor, pass it to the Actor, fulfill that Promise with a new Promise created inside the Actor, fulfill the new Promise with a value, stop the Actor, then try to read the value of the Promise, I get an error that my queue is invalid. I think this has something to do with the queue for the Promise being created inside the stopped Actor's thread but I'm not entirely sure. Anybody have any ideas?

Capture.PNG

AristosQueue (NI)
NI Employee (retired)

> Anybody have any ideas?

 

Refnum has to be created by the receiver, not the sender. So you can't create a new promise inside an actor unless the actor is guaranteed to outlive the promise receiver. Period, full stop, no workaround known.

crabstew
Member

Refnum has to be created by the receiver, not the sender.

That's what I figured, thanks for the confirmation.

 

I *think* I have a workaround specifically for the case of fulfilling a Promise created outside the Actor with a Future created inside the Actor. Basically, there can be a synchronous callback that updates all Futures that depend on the current Future with a value. Will take some rework (mainly adding support for a synchronous callback and "protecting" the original queue while swapping out the value) but it should "collapse" the scope of the original Promise, so to speak. Probably won't have time to look at it until this weekend though.

crabstew
Member

Added workaround for problem mentioned above. Also rolled the Reject Promise VI into the Fulfill Promise VI. No need for a separate VI in LabVIEW when we already have the error wire.

 

Added a bunch of unit tests using JKI's Caraya. If anyone can think of some more weird corner cases that break anything (or should be tested for in general), please feel free to add unit tests for them even if you don't have the time to fix them.

 

I'm pretty sure there is a lot of room for optimization (such as not creating the callback and writer queues and semaphore until they are actually needed) but I think I'm pretty happy with the current API. I'll add some documentation to the repository some time in the next week. Still definitely looking for feedback!