Bug with local scope, cfthread, and ColdFusion 9

This post is more than 2 years old.

So this is a weird one. Please note that myself (and the other guys working on this) are still digging in so we may be wrong, but it appears that the use of cfthread in a CFC method, along with the use of local scope, is causing a bug in ColdFusion 9. For an example, see the code below.

<cffunction name="test"> <cfset local = structNew()>

&lt;cfthread name="doogus" action="run"&gt;

&lt;cfquery name="local.foo" datasource="ormtest"&gt;
select distinct id, name
from [user]
&lt;cfdump var="#local#"&gt;
&lt;cfreturn valueList(local.foo.id)&gt;


<cfoutput>#test()# #now()#</cfoutput><p>

As you can see, I've got a method, test, than runs a thread and then does a query. Notice I use the new local scope so I don't have to var it above. Upon running this, we are seeing an error when we try to use the valueList function. The error states that the parameter passed to valueList is invalid. Even more interesting, the dump shows only arguments.

But wait - it gets better. Both myself and Tony Nelson and others confirmed that if you run this code you will get the right result about 40% of the time. Essentially a bit less than half. On those runs, the local scope shows the query and the return runs fine.

The fix? Don't use local.whatever. If I switch to var foo = "" and then just use foo instead of local.foo, it works.

So.... any thoughts on this? I've filed bug 82861 for this.

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Raymond Camden posted on 5/13/2010 at 7:51 PM

New details. We discovered that when we dumped the cfthread scope, and we applied load, it worked when the thread did NOT start before the query, which would happen when CF needs to fire that thread later (again, due to load).

Hope this makes sense.

Comment 2 by Sean Coyne posted on 5/13/2010 at 7:55 PM

I ran the code 100 times against the cfartgallery database and could not recreate the issue. Very odd.

Comment 3 by Ben Nadel posted on 5/13/2010 at 9:00 PM

I am not sure I understand this line:

<cfset local = structNew() />

In ColdFusion 9, with the new implicit local scope, isn't that really doing the following:

<cfset local.local = structNew() />

If I had to guess what was going on, CFThread executes as a function. In the 40% of the time that the CFThread actually executes inline (rather than being queued until after the query executes), the establishment of the thread's local scope (which should also exist), is somehow messing up the local scope of the parent function.

Arg. I don't have CF9 installed to play with :(

Comment 4 by Ben Nadel posted on 5/13/2010 at 9:01 PM

Thinking about it again, isn't the <cfset local = {} /> just creating "local" as a Variables-scoped value now?

Comment 5 by Raymond Camden posted on 5/13/2010 at 9:25 PM

@Ben: Typo. Adding var in front doesn't change anything.

Comment 6 by Ben Nadel posted on 5/13/2010 at 9:30 PM

Hmm. It's got to be the CFThread local scope interaction. If you put CFThread[action=join] right after the CFThread, does it cause the error every single time?

Comment 7 by Raymond Camden posted on 5/13/2010 at 9:32 PM

Joining doesn't help. See my first comment - which is a bit vague. On load, I noticed that the code worked when the thread status was not started. So as soon the thread starts, whether it ends or not, the issue occurs.

Comment 8 by Ben Nadel posted on 5/13/2010 at 9:34 PM

Ah, gotcha. Yeah, I was a bit confused by "load".

Comment 9 by Sumit Verma posted on 5/14/2010 at 9:50 AM

Interesting. I was able to reproduce the error with even simpler code:
<cffunction name="test">
<cfthread name="doogus" action="run">
<cfset var foo = {} />
<cfset local.foo.id = 1 />
<cfdump var="#local.foo.id#">

If I change the line <cfset local.foo.id = 1 /> to <cfset foo.id = 1 /> it never errors. So, accessing local scope seems to be fine, but when we set it after cfthread it seems to cause issues...

The easiest way to reproduce the error is to make a change in the code (just add some space or extra line) and it will error on the first try. After that it errors out on random.

Also, If we output metadata of local or foo after cfthread, it's always correct.

Comment 10 by Sumit Verma posted on 5/14/2010 at 9:59 AM

Actually, Ben is absolutely right! Joining the thread right after CFThread causes error every time...

So, basically the local scope gets wiped out when the thread completes.

If I do a dump of local scope after thread join, error goes away, so, I think local scope gets re-created when we do a dump of it.

Comment 11 by Sumit Verma posted on 5/14/2010 at 10:06 AM

Correction to: "the local scope gets wiped out when the thread completes."

Should read: "all the items inside local scope gets wiped clean when the thread completes."

Something like that... too late in the night...

Comment 12 by Raymond Camden posted on 5/14/2010 at 2:11 PM

I disagree - in my testing, it isn't thread completion, but thread starting. Whether it ended or was still running, I saw the error. If it doesn't start (again, with load), then it doesn't error.

Comment 13 by Ben Nadel posted on 5/14/2010 at 3:51 PM

I should have CF9 installed later today (Jamie Krug is helping me set up a multi-instance local dev environment - very exciting!) so I'll be able to play.

The purpose of the CFThread[join] was not to make sure the thread ended, but rather that is *started*. Since threading is asynchronous, it doesn't have to start where it's defined. The join was simply to make sure that it did start (be enforcing that it also ended).

This is a very curious problem!

Comment 14 by Martijn van der Woud posted on 5/20/2010 at 11:03 AM

My guess would be that the thead's local scope (initiated when the thread starts executing, presumably) somehow overwrites the local scope of the function. Definitely looks like a bug to me.