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.
<cfthread name="doogus" action="run">
</cfthread> <cfquery name="local.foo" datasource="ormtest">
select distinct id, name
from [user]
</cfquery>
<cfdump var="#local#">
<cfreturn valueList(local.foo.id)>
</cffunction> <cfoutput>#test()# #now()#</cfoutput><p>
<cffunction name="test">
<cfset local = structNew()>
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.
Archived Comments
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.
I ran the code 100 times against the cfartgallery database and could not recreate the issue. Very odd.
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 :(
Thinking about it again, isn't the <cfset local = {} /> just creating "local" as a Variables-scoped value now?
@Ben: Typo. Adding var in front doesn't change anything.
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?
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.
Ah, gotcha. Yeah, I was a bit confused by "load".
Interesting. I was able to reproduce the error with even simpler code:
<cffunction name="test">
<cfthread name="doogus" action="run">
</cfthread>
<cfset var foo = {} />
<cfset local.foo.id = 1 />
<cfdump var="#local.foo.id#">
</cffunction>
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.
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.
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...
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.
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!
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.