Recursive / Nested CFThreads Can Get Around CFSetting RequestTimeout In Lucee CFML 5.3.7.47
A couple of months, I demonstrated that the request timeout of the parent page also affects the execution of CFThread
tags in that same request in Lucee CFML. To get around that, within that post, we were using the CFSetting
tag to override the requestTimeout
value from within the asynchronous thread. In yesterday's post, I started to look at implementing "tail recursion" using CFThread
tags in Lucee CFML. Which got me wondering as to how recursive and / or nested CFThread
tags would interact with the requestTimeout
setting in Lucee CFML 5.3.7.47.
To test this, I created a ColdFusion page with a 2-second request timeout. Then, I spawned two sets of CFThread
tags:
A "control" thread that will run for 10-seconds which we are expecting to timeout and get interrupted - the "known case", so to speak.
A "recursive" thread that will only run for 1-second - less that the
requestTimeout
value; but, which will call itself 10-times, recursively.
The goal here being to see if the 10-second recursive thread gets interrupted? Or, if it's allowed to finish:
<cfscript>
// Let's set the overall request timeout to 2-seconds. This affects both the top-
// level page as well as any asynchronous CFThread tags spawned during this request.
setting
requestTimeout = 2
;
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
// Let's spawn a "control" thread - one that we know will run longer than the
// designated timeout. We're EXPECTING this thread to timeout and be INTERRUPTED.
thread
name = "controlThread"
cutoffAt = ( getTickCount() + 10000 )
{
systemOutput( "Spawning control thread.", true, true );
while ( getTickCount() < cutoffAt ) {
sleep( 50 );
}
systemOutput( "Control thread completed.", true, true );
}
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
recurseThread( 10 );
// Now, let's spawn a series of recursive threads. Each individual thread will run
// for LESS THAN the request TIMEOUT. However, in aggregate, all of the recursive
// threads will run for MORE THAN the request timeout.
public void function recurseThread( required numeric count ) {
thread
name = "recursiveThread.#createUniqueId()#"
count = count
{
systemOutput( "Spawning recursive thread for count, #count#.", true, true );
sleep( 1000 );
if ( count <= 0 ) {
systemOutput( "Recursive thread completed.", true, true );
return;
}
recurseThread( count - 1 );
}
}
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
echo( "Top-level page done." );
</cfscript>
Now, if we run this ColdFusion code, we get the following terminal output:
As you can (sort of) see, the "control" CFThread
execution is interrupted by the ColdFusion runtime a few seconds after the request is initiated. This is because it exceeded the 2-second requestTimeout
setting for the request. The recursive CFThread
, on the other hand, is allowed to complete its execution. This is because each invocation of the recursive thread only ran for about 1-second, despite the fact that - in aggregate - the recursive algorithm far exceeded the 2-second requestTimeout
setting.
There's nothing too shocking in this post. This exploration is really just laying the ground-work for asynchronous processing that I want to do in Lucee CFML. And, it's important to understand the difference between a "long running algorithm" and a "long running thread"; and how that difference plays-out in terms of request timeouts.
Want to use code from this post? Check out the license.
Reader Comments