Skip to main content
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Dave Ferguson and Simon Free and Tim Cunningham and Jason Dean
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Dave Ferguson ( @dfgrumpy ) Simon Free ( @simonfree ) Tim Cunningham ( @TimCunningham71 ) Jason Dean ( @JasonPDean )

Recursive / Nested CFThreads Can Get Around CFSetting RequestTimeout In Lucee CFML 5.3.7.47

By on
Tags:

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:

Control and Recursive CFThreads being run in a short request timeout in Lucee CFML.

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

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel