ColdFusion 9 catch() is not thread-safe!

I almost hate to admit this… no, I really hate to admit this.   For some reason, I was have always been under the impression that when you do a catch() in CFSCRIPT, that the variable you define as the catch is protected within the catch condition.   However, it hit me today that it is written to the variables scope by default.  Not only that, as I was testing it further, I believe that I have discovered that it is not thread-safe at all!   (edit: this problem applies to CFCATCH too. See notes at bottom and in comments)

Want to test this?  Open up a dummy CFM file and run the following:

(edit: I have modified this example since originally posting, putting the try/catch within a method so that it is consistent with the other examples)

WriteDump(ourFunction());	

public void function ourFunction()	{
	try	{
		local.a = b;
	}
	catch( any e )	{
		killE();
		WriteDump(e);
	}
}

public void function killE()	{
	StructDelete(variables,"e");
}

In case what we are doing isn’t abundantly obvious, we are creating a forced exception by referencing variable “b” which doesn’t exist.  In the catch(), we are saving the exception structure as variable “e”  so that we can handle however our business rules dictate.  In the case of our example, we are calling a method called killE(), and as you see it deletes “e” from the variables scope of the current template.  In the following line in catch() we are going to dump out exception details.  However, rather than a nice exception telling us that “b” wasn’t defined, we get the following:

ColdFusion exception

So, all we need to do is change “e” to “local.e” right and it will be thread-safe right?

WRONG!

This is apparently invalid syntax in Adobe ColdFusion (as of v.9).  Take this example:

WriteDump(ourFunction());	

public void function ourFunction()	{
	try	{
		local.a = b;
	}
	catch( any local.e )	{
		killE();
		WriteDump(local.e);
	}
}

public void function killE()	{
	StructDelete(variables,"e");
}

When we run this example, the following occurs:

What?!  So apparently we have to use the “var” scope to define our exception then, right?  So how about this attempt… this should do it, right?

WriteDump(ourFunction());	

public void function ourFunction()	{
	var e = "";

	try	{
		local.a = b;
	}
	catch( any e )	{
		killE();
		WriteDump(e);
	}
}

public void function killE()	{
	StructDelete(variables,"e");
}

WRONG!

If we take this approach, we receive the following:

My impression of this exception is that it is trying to write a variable into the variables scope that is already defined in the var scope and barking about it.

So then, what are we left with?

I am admittedly not the sharpest crayon in the box, but to me this indicates that catch() is 100% not thread-safe!   If anyone sees any problem with this diagnosis that I have made or has any thoughts, I am all ears.  If this is true, this throws huge wrenches into my work, and I am sure it does to others as well.

EDIT: As has been pointed out by Henry, this is an issue that Adobe was notified of this bug July of 2010 – almost 16 months!  It affects at least versions 8 and 9, and is not limited to CFSCRIPT.  It apparently affects CFCATCH as well.  I have looked through the tech notes of ColdFusion 9.0.1 Cumulative Hot Fix 1 and ColdFusion 9.0.1 Cumulative Hot Fix 2 and find no evidence that they have ever addressed it.

EDIT (again): I have tested this in both Railo and OpenBD.  OpenBD, due to the fact that it puts variables within methods in the var scope by default does not fail on the first test.  Railo, does fail.  However by using the variables scope like ColdFusion.  However, it allows you to do catch( any local.e ) and var scope e ahead.  So in a nutshell, ColdFusion is the only engine that does not give us any way to do thread-safe error handling.

13 thoughts on “ColdFusion 9 catch() is not thread-safe!

  1. Ah, I was about to go file a bug! I didn’t realize this was common knowledge. I have never heard of this before and am quite honestly blown away and trying to calculate how many places in my code that are susceptible to this. I can’t believe that bug report is almost a year and half old and it hasn’t been addressed with a patch! How is that OK by any standards?

  2. nope, Cannot var-scope CFCATCH in CFML neither. Sean’s comment does not apply to 9,0,1,274733. Not sure about CF8…

    “coldfusion.compiler.ASTvariableDefinition$CannotRedefineException: Cannot declare local variable CFCATCH twice.”

  3. Henry, take a look at my last edit. I did some testing with OpenBD and Railo. In what seems to be a trend, Railo is again doing things better than ACF.

  4. I can reproduce the race condition, but only when the URL is slightly different (coldbox’s issue? don’t know).

    The only workaround I can think of is… do not use cftry/cfcatch in a singleton CFC… :( horrible.

  5. Hey Dave, Alan here from http://openbd.org/

    The example at the top if you place that in a top level file/scope then there is no “local” scope as they share the scope of the page they are living in. So the first example you highlight, if put in a single CFM file, then I expect it to fail. All is well there.

    However, the example where you have that logic wrapped inside a ourFunction() works perfectly in OpenBD. It will save the cfcatch variable, e, in the local scope. I just ran it with the latest nightly build. We have no need for the Railo hack of “local.e” as it is doing what you would expect it to do.

    Incidentally you can turn off this need for ‘var’ inside your CFC’s with OpenBD and have it operate like other languages. This was always one of those bizarre introductions ACF introduced that still to this day baffles me. Example: http://openbd.org/manual/?/engine_bluedragonxml#functionscopedvariables

  6. Alan, thank you very much for responding and pointing out the problem with my first test. I have modded the first test so that it is consistent with the others. What it shows is that OpenBD is the only engine that treats it as I would hope! I have modded the last edit to say: “EDIT (again): I have tested this in both Railo and OpenBD. OpenBD, due to the fact that it puts variables within methods in the var scope by default does not fail on the first test. Railo, does fail. However by using the variables scope like ColdFusion. However, it allows you to do catch( any local.e ) and var scope e ahead. So in a nutshell, ColdFusion is the only engine that does not give us any way to do thread-safe error handling.”

    • Dave, Railo also has the option to not require var scoping (named “local scope mode” in Admin>Settings>Scope page) – if you set this to “always” then it should behave the same as OpenBD (i.e. always place new variables in functions in the local scope instead of variables scope.)

  7. That is crazy nuts. First, why not allow you to define where that catch variable goes? Secondly, just about every other language in the world defaults to a local variable scope for *ANY* variable defined in a method (notice I said *just about every*, and not *all*), so why can’t CF? And of course there is no semephore mechanism to control access to volatile variables…

    Lesson here? High traffic sites, shouldn’t use best practices of exception handling for fear of race conditions.

  8. There is a setting in the Railo admin which exists for a while already that you can have Railo put the variables into the local scope by default and not always. In this way you can make use of both behaviors, depending on which you need in which context. And using local.e is a perfectly valid syntax. You wouldn’t have to write variables.e if you need it there in the other case…

    Gert

  9. In my quick test I was not able to have the “foo” variable generated by catch(any foo) actually overwrite an existing variables.foo in the same instance of the CFC – if your finding holds true, shouldn’t it? For instance, in this code if you run makeVariable, then dump, then run generateException on the same instance and then dump getVariablesScope again the original variables.foo is still there:

    try{
    1/0;
    }
    catch(any foo){
    variables.exceptionCalledFoo = foo;
    }

    • Hmm, guess it stripped out instead of escaping the tags. The code snippet should be:

      <cfcomponent output=”false”>
      <cffunction name=”makeVariable”>
      <cfset variables.foo = “I was made inside the makeVariable function on #now()#” >
      </cffunction>

      <cffunction name=”getVariablesScope”>
      <cfreturn variables>
      </cffunction>

      <cffunction name=”generateException”>
      <cfscript>
      try{
      1/0;
      }
      catch(any foo){
      variables.exceptionCalledFoo = foo;
      }
      </cfscript>
      </cffunction>
      </cfcomponent>

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>