Language Idea: CFThread completion callback

One nice thing (in theory) about the runAsync() function is that you can register callbacks for when the complete (either in success or in error). I don’t use that function much because I remember it having a lot of issues and edge-cases when I last tried it. But, it got me thinking - it would be cool if you could register a completion callback on the CFThread tag. Imagine something like:

<cfscript>

	thread
		name = "MyThread"
		action = "run"
		attributeOne = "Hello"
		attributeTwo = "World"
		onThreadStop = handleThreadCompletion // CLOSURE / METHOD reference.
		{

		// Do some groovy async stuff....

	}


	private void function handleThreadCompletion(
		required string threadName, // Name of the thread.
		required struct threadStruct, // Reference to thread, ie cfthread[name].
		required struct threadScope // Reference to thread scope, cfthread.
		) {

		systemOutput( "Thread #threadName# finished with state #threadStruct.status#" );

		if ( threadStruct.keyExists( "error" ) ) {

			systemOutput( "And had an error" );

		}

	}

</cfscript>

I think this could open up some interesting async functionality. Especially since handle the thread completion wouldn’t become a blocking operation (like it does in runAsync()).

Thoughts?

2 Likes

I know Lucee has been ideating on “Listener” techniques, ala Query Listeners (docs link). So, maybe it would make sense to have something like a “Thread Listener”. Though, I think it would really only make sense on a per-thread basis (ie, not a app-wide listener for all threads).

The idea of callbacks on thread completion is cool, but I think as-presented here is very limiting. I would recommend checking into the prior art here, which is completable futures in Java, which Luis wrapped up into CFML several years ago. It’s much much more powerful and follows a functional approach to threading, unlike cfthread.

There are tons of examples in that repo I linked to and you can use all of them today in Adobe CF and Lucee Server. I’ll also note, ColdBox is not a requirement to use any of this. It’s also bundled in stand-alone WireBox, CacheBox, and/or LogBox.

I’ll look at the CB Future stuff. I know I’ve poked around in it in the past, but I don’t remember too much. I accept that cfthread is limited; but, it is also super simple, which is a nice trade-off in many cases. But, clearly, I want to explore outside those cases, which is where I definitely do bump into the limits.

Anyway, I’ll take another look :muscle:

If you want “super simple”, then honestly what’s wrong with this:

thread {
  // Some code

  // Some other code that runs on the completion of the code above
}

I mean, that’s what you want, right? Is there really a difference between passing a UDF to the thread tag to run after the other code in the body of the thread tag is done? You may say there’s benefit to having it in a UDF. If so, then just do this

function callMeNext() {
  // Some other code that runs on the completion of the code above
}

thread {
  // Some code

  callMeNext();
}

Totally, my concern is perhaps more about thread failures, like “Sudden thread death”. Or, perhaps if a thread can’t even be spawned in the first place due thread exhaustion.

I have no idea what you’re talking about, lol. There is no such thing in the JVM. Lucee may attempt to stop threads that have run longer than you allow (I’m not 100% sure if that applies in CFThread-land TBH) but historically, Lucee’s stance on that has been that you don’t get to run any additional error handling in those cases. The long-running thread has worn out its welcome and is done executing any more code.

If you’re just worried about runtime errors, then use try/catch.

This also is a non-existent thing-- at least as far as cfthread is concerned. When using Java thread pools, you’ll hear that term used, but to my knowledge cfthreads are not part of a pool and the JVM itself has no limit of how many threads it can have. Now, you may literally run the heap out of memory to the point where no more threads can be created, but in that scenario, your callback won’t be likely to run anyway.

It’s also worth noting here Adobe CF has a limit of how many cfthreads can run at once (it queues the extras) but Lucee only has a “Concurrent Requests” setting which I don’t thin limits cfthreads in any way.

Hmmm, interesting. It’s very possible that some of my mental model here is from the Adobe CF8 days or something and might not be relevant anymore. I’m 99% sure that I’ve seen the term “sudden thread death” show up in my error logs. Though, I can’t remember the last time I’ve seen it. I wonder if it is related to K8 pods spinning down in production; or, someone terminating a CFThread instance in something like FusionReactor. I think I’ve also seen something like a “Thread interrupted” exception show up.

Maybe I should just stop worry about this kind of thing :person_shrugging:

yeah, a lot, scratch that, all the old rules we old CF devs have need to be challenged

back back to the proposal, I think it’s a good, simple idea, an error handler would be rather useful too

1 Like

Sometimes, us old dogs know too many tricks that aren’t relevant any more :laughing:

At least Lucee lets us spawn “nested” threads; I think ACF still has that limitation.

1 Like