Skip to main content
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Matt Vickers and Jonathan Dowdle and Joel Hill
Ben Nadel at cf.Objective() 2014 (Bloomington, MN) with: Matt Vickers Jonathan Dowdle Joel Hill

Calling Into A Timed-Out Parent Page Context From A CFThread Tag In Lucee CFML 5.3.6.61

By
Published in

Yesterday, I looked at how you can eagerly show report-generation results from a CFThread tag in Lucee CFML. However, after I was done with that experiment, it got me thinking about what would happen if a long-running CFThread tag called back into a parent page context after the parent page had timed-out. This is just a quick sanity check to make sure that this will work as one might hope in Lucee CFML 5.3.6.61.

As I attempted (poorly) to demonstrate a few months ago, the CFSetting tag can override the request-timeout within a CFThread tag in Lucee CFML. This means that even if a parent page were given a small request-timeout - which would inherently affect any CFThread tag spawned during that request - we can extend the life-span of the CFThread tag beyond the life-span of the parent page.

The question then becomes, can that extended-timeout CFThread tag still call back into the parent page context once that parent page has timed-out? "Page Context" is kind of a funky beast in ColdFusion; so, I don't necessarily know which edge-cases I need to worry about. For the moment, I'm just going to see if I can call a User Defined Function (UDF) that is defined in the parent context; and, that I can reference and mutate variables in that context:

<cfscript>

	variables.variablesCheck = "It works!";
	request.requestCheck = "It works!";

	/**
	* I exist-in and reference values in the parent page context. This function will be
	* called AFTER the parent request has timed-out.
	*/
	public void function doSomethingInParentPageContext() {

		// Try to modify a value in the parent page context.
		variables.variablesCheck &= " Heck yeah!";

		systemOutput( "Called doSomethingInParentPageContext()", true );
		systemOutput( "Variables check: #variables.variablesCheck#", true );
		systemOutput( "Request check: #request.requestCheck#", true );

	}

	/**
	* I block the current thread for ROUGHLY the given duration using an incremental
	* sleep() operation. The point here is that the internal loop gives the COldFusion
	* runtime an opportunity to kill the thread if need-be.
	* 
	* @durationInMilliseconds I am the duration in milliseconds to block.
	*/
	public void function blockFor( required numeric durationInMilliseconds ) {

		var cutoffAt = ( getTickCount() + durationInMilliseconds );

		while ( getTickCount() < cutoffAt ) {

			sleep( 20 );

		}

	}

	// ------------------------------------------------------------------------------- //
	// ------------------------------------------------------------------------------- //

	// This CFThread is going to be spawned and then OUTLAST the duration of the parent
	// page request. Then, it will make a call BACK INTO THE PARENT PAGE CONTEXT so that
	// we can test to make sure that works as one might hope.
	thread name = "test" {

		// Override the request-timeout for the CFThread tag (to be longer than the
		// request-timeout for the parent page context).
		setting requestTimeout = 20;

		blockFor( 10 * 1000 );
		doSomethingInParentPageContext();

	}

	// ------------------------------------------------------------------------------- //
	// ------------------------------------------------------------------------------- //

	// We're going to set the parent page to have a relatively small timeout. THen, we're
	// going to use a blocking function below to make sure that the parent page duration
	// exceeds the timeout setting.
	setting requestTimeout = 2;

	blockFor( 10 * 1000 );
	echo( "Done." );

</cfscript>

As you can see, the parent-page request is set to timeout in roughly 2-seconds. But, it spawns an asynchronous CFThread that overrides its own timeout to be 20-seconds. Then, after the parent-page times-out, the CFThread tag invokes the function doSomethingInParentPageContext(), which is defined in, references, and mutates values in the parent page context.

And, when we run this ColdFusion code, we get the following output:

A long-running CFThread tag calling back into a timed-out parent page in Lucee CFML.

As you can see, the top-level page request times-out in a few seconds. Then, after about 10-seconds, the CFThread tag invokes a function defined in the parent page context. And, everything works swimmingly!

Again, "page context" is a complicated topic in ColdFusion. So, I am sure this is not an exhaustive test of what can go wrong. But, this should probably cover most of my immediate use-cases, which are pretty much limited to a CFThread tag calling methods in a parent page (a ColdFusion Component in my typical case). Good to know this "just works" in Lucee CFML 5.3.6.61.

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