Skip to main content
Ben Nadel at CFinNC 2009 (Raleigh, North Carolina) with: Daria Norris
Ben Nadel at CFinNC 2009 (Raleigh, North Carolina) with: Daria Norris

Checking For Nested CFThread Execution In Adobe ColdFusion

By
Published in Comments (5)

Years ago, when Adobe ColdFusion introduced the CFThread tag for effortless asynchronous programming in CFML, they made a decision to not allow your application to spawn nested threads. I believe most people view this as a misguided decision (though well-intentioned). And yet, this constraint still exists in the latest ColdFusion 2025 release which came out yesterday.

I coincidentally stumbled over this issue yesterday as well (in Adobe ColdFusion 2021) while trying to log an error to BugSnag. My error-logger makes the HTTP call to the remote BugSnag API inside a thread so that it's non-blocking for the calling context. But, in this case, the calling context was also in a thread. So, my low-level error logging failed with the ColdFusion message:

Child threads are not supported.

Using getPageContext() Undocumented Methods

Although Lucee CFML never implemented this constraint, they do provide a built-in function that allows you to check if you're currently running in a threaded context: IsInThread(). Adobe ColdFusion doesn't have this; but, since Lucee is open-source, I wanted to see how they implemented it:

public static boolean call(PageContext pc) {
	PageContext root = ((PageContextImpl) pc).getRootPageContext();
	return root != null && root != pc;
}

Unfortunately, Adobe ColdFusion doesn't have a .getRootPageContext() method; however, when I was dumping-out the getPageContext() object, I noticed another method named: isUnderCFThread(). Some trial-and-error seems to indicate that this ACF method operates in the same way. Which means, we can use the isUnderCFThread() method to polyfill Lucee's IsInThread() functionality.

To see this in action, I'll try this polyfill both inside and outside of a CFThread tag:

<cfscript>

	/**
	* Polyfill Lucee CFML's isInThread() function using Adobe ColdFusion's page-context.
	*/
	public boolean function isInThread() {

		return getPageContext().isUnderCFThread();

	}

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

	// Check before thread.
	results = [:];
	results.beforeThread = isInThread();

	// Check mid thread.
	thread name = "one" {
		thread.midThread = isInThread();
		sleep( 500 );
	}

	// Check after thread (but while thread is still running / sleeping).
	results.afterThread = isInThread();

	// Read results for thread after it joins.
	thread action="join";
	results.midThread = cfthread.one.midThread;

	writeDump( results );

</cfscript>

We we run this, we get the following output:

Struct output showing isInThread() call results.

As you can see, the call both before and after the CFThread tag result in a No; and, the call made whilst inside the CFThread tag results in a Yes. Obviously, this is a limited test case; so there may be edge-cases that exhibit different behaviors. But, at least this gives us engineers in the Adobe ColdFusion world something easy to try.

Until, hopefully one day, Adobe ColdFusion remove this limitation.

Another Hack With callstackGet()

If you don't love the idea of reaching into the getPageContext() black box, we might be able to leverage the callstackGet() function. This provides a stack-trace from the current line of code. We can walk up this stack-trace and look for function names that indicate a CFThread context:

<cfscript>

	/**
	* Polyfill Lucee CFML's isInThread() function using Adobe ColdFusion's call-stack.
	*/
	public boolean function isInThread() {

		for ( var frame in callstackGet() ) {

			// ColdFusion implements the CFThread tag as a hidden function. It names
			// these functions consistently enough that we can use the name to determine
			// if we're in a deep thread context.
			if ( frame.function.findNoCase( "_CFFUNCCFTHREAD_" ) ) {

				return true;

			}

		}

		return false;

	}

	// ... rest of demo the same ...

</cfscript>

Mark Mandel's Approach From 2007

Back in 2007, Mark Mandel shared an approach for testing CFThread context by looking at the "current Java thread group." To be honest, I don't really know what that means (I'm not a Java guy); but, his idea from 2007 still seems to work in ACF 2021 (I can't test on CFFiddle.org since they don't allow low-level Java objects to be created). His site isn't online; but, a copy of his original article can be found on the Web Archive.

Here's a polyfill based on his technique:

<cfscript>

	/**
	* Polyfill Lucee CFML's isInThread() function using Java Thread Groups.
	* 
	* This idea was created by Mark Mandel in 2007. But, his page doesn't seem to be
	* accessible anymore; so here's a link to the Web Archive:
	* 
	* https://web.archive.org/web/20220520073629/https://www.compoundtheory.com/how-to-tell-if-code-is-being-run-inside-a-cfthread-tag/
	*/
	public boolean function isInThread() {

		var currentThreadGroup = createObject( "java", "java.lang.Thread" )
			.currentThread()
			.getThreadGroup()
			.getName()
		;

		return ( currentThreadGroup == "cfthread" );

	}

	// ... rest of demo the same ...

</cfscript>

Using Brute-Force try/catch

If you really don't want to use anything that is undocumented for fear that it might break with a server update, you can always use a brute-force technique. In this case, we can simply try spawning a temporary thread and seeing if it throws an error:

<cfscript>

	/**
	* Polyfill Lucee CFML's isInThread() function using try/catch.
	*/
	public boolean function isInThread() {

		try {

			thread name = createUuid() priority = "low" {
				// ... don't care, just seeing if an error was thrown ...
			}

			return false;

		} catch ( any error ) {

			return true;

		}

	}

	// ... rest of demo the same ...

</cfscript>

Here, if the CFThread tag declaration runs without error then we know that we're not currently in a child-thread context.

It seems like this feature would be simple to add to the core CFML specification. If there's not already a feature request for it, I'll file one.

Want to use code from this post? Check out the license.

Reader Comments

8 Comments

I've asked and asked Adobe to allow child threads. Even made a request during ACF2025 beta. Fell on deaf ears. Seems like something a modern language should allow. Please Adobe remove this silly restriction! Just like you suggest Ben we have third party calls that can be spawned off and they might spawn others etc.

15,983 Comments

@All,

I added a brute-force example to the end of the post - basically just trying to spawn a throw-away thread in order to see if an exception is raised. While this is an absurd idea, it's really the only idea that's relies on fully documented behaviors. The rest of the ideas all rely on coincidental behaviors of the application server.

Post A Comment — I'd Love To Hear From You!

Post a Comment

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