Friday, 11 October 2013

CFML: A possible variation on the thread idea...

G'day:
I can't take sole credit for this idea, because when reading something Luis said today on the Railo Google Group, I had an epiphany. Let's have a look at <cfthread> (or, indeed, just thread) again...

A coupla weeks back I raised an E/R for ColdFusion: "Another suggested tweak to CFScript: thread" (3644151). This dealt with a syntactical change to thread to make it work more sensibly (IMO, anyhow), and in-keeping with the general syntactical approach to CFScript.


Today Luis posted this on the Railo Google Group: "Add closure support to blocking operations to allow them to be non-blocking operations", as well as an E/R on the ColdFusion bug tracker: 3648785.

This reminded me of a conversation I had on the #ColdFusion channel on IRC a few days ago, when discussing my E/R, and people were suggesting that CFML's threading approach was close to useless as it didn't offer enough control or functionality compared to using something like CFConcurrent. This position intrigued me, and makes a degree of sense. The analogy here is that <cfthread> is to CFConcurrent what java.lang.Thread is to java.util.Concurrent. I didn't draw any particular hard and fast conclusions from this - otherwise you'd've read about 'em here - but it did make me think that my thread ideas needed further thought.

Luis's idea is focused on specific CFML functionality which can take time to execute: file ops, DB ops, etc. I think he's right, but I think his suggested approach is too focused. Obviously those are candidates for async / non-blocking operations, but so could any operation. Including ones we write in our own code. But I did like his idea of offering a callback for when the process completes. This is something that CFML's thread operations lack: we can't tell when they finish, because the CFML engine doesn't tell us.

So combining my threading thoughts with Luis's callback thoughts, I came up with this idea:

t1 = Thread.new(optional struct dataToPass, optional struct eventHandlers){
    // code to run
}

This is the same as my suggestion the other week, except now we pass a struct of event handlers. This would be have (optional) keys of success, fail, error, which themselves are functions which are passed [whatever, I didn't get that far; obviously the error one would get at least the error object], and are called if the code in the thread block completes, if it does not complete, or if it errors (respectively).

This got me to thinking "OK, so how do we differentiate between success and fail?". The easiest way would be to have the thread code return true or false. Which then makes it seem like a function, not a code block. Then the penny dropped... it should be a function! So I revised my idea to this:

t1 = Thread.new(required function functionToRun, optional struct dataToPass, optional struct eventHandlers);

I think that's much better. What this means is the functionToRun can have your fileRead() / fileWrite() / DB call / whatever... it caters to any situation in which one might want to fire code, but not necessarily forget it.

This would also have the benefit of not needing re-architecture to allow block operations to return values... it's all just a function, and CFML already does this.

What do you think about this? Shall I E/R it for both ColdFusion and Railo? Fine-tune it some more?

Righto.

--
Adam