Thread safety and the var scope – live example

by Mike on July 21, 2008

Recently, I've talked to a few developers who knew about the var scope, but didn't quite grasp the concept of making a variable local to a function. Additionally, I've come across a few posts that indicated that setting a var statement was strictly to conserve memory (along with other reasons that missed the point).

To help anyone struggling with understanding the impact of the var scope I've put together a quick example to demonstrate what can happen when you improperly scope a variable.

The example here focuses on CFCs that are loaded into a shared scope (session, application, etc), and are accessed simultaneously by multiple users.Running the code will make 2 simultaneous requests to the same function in separate iframes. The example contains a loop that outputs the index of a loop from 1 to 100. If the loop executes correctly the numbers appear in green, if the numbers are out of sequence, they will be red. To make the results more dramatic I've included a 1-40ms pause in each loop iteration.

Here's the cfc that is loaded in server scope (simplified for reading). Note the only difference in the functions is that the "count" variable has a var statement in one function and doesn't in the other function.

If the example executed correctly, you should have seen all green numbers in both windows for the var scope example, and a mix of green and red in the example that executed the function without the var statement. The presence of the "var" statement makes the variable local to the function. This means that when the function runs, it creates a new instance of the variable that is unique each time the function is executed. Without the var statement in place, the variable gets "shared" between everyone who is accessing this component. When a component lives in a shared scope (session, application) the variable will be shared for everyone accessing that scope. Think of the following scenario.

User 1: set count = 1

User 2: set count = 1

User 1: set count = count + 1

User 2: set count = count + 1

User 1: count = 2

User 2: count = 3

In this case the developer most likely intended to have both users see count = 2, however the variable was shared and as a result a user's result was impacted by another user's request. Note: this may be the intended behavior of the code, and that's why there is no definite rule on why you should "always" or "never" var scope a variable.

Unfortunately, recognizing issues that appear as a result of improper scope can be difficult. Many times they result in server errors that are intermittent and only appear under load. This can sometimes make it practically impossible to reproduce the error in a test environment. Also, many tags create variables that are easy to miss (cfquery, cfloop, etc).

Luckily, there's a tool to help (shameless plug here). Take a look at varscoper. Hopefully this will become an integral tool in your development process.

I’ve lived this problem and since then var scope all my variables so something like that doesn’t happen, but there are a bunch cftags <cfquery> for instance that you give a variable name and it returns the query object into your variable. I was always under the impression that the name we give to our queries was automatically var scoped and local to the function where it was called, unfortunately that too was wrong and to protect your queries you need to also var scope the variable that you will be using for your query too.

The newer version of Railo can automatically set unscoped variables declared inside of functions to the var scope, and theres some talk of integrating this into CF 9. Hopefully openBD will follow suit too. In the meantime, use Varscoper as mike suggested, but hopefully in a year or two this will be a thing of the past for all CF development (on newer platforms at least)

Wow. This tool just saved me a ton of time. I have just gotten back into CF programming after not touching it for a couple years, and back then I never really wrote CFCs…I just wrote CFM pages and used CFINCLUDEs and it was messy. Now I’m trying to do things "right" and use functions inside CFCs. Well, everything was peachy until I wrote my first recursive function and everything went haywire. No errors, but it certainly wasn’t doing what I expected. A little research, varscoper, and 10 minutes to copy and paste the CFSETs into my functions…and everything is peachy again. I think I’m pretty good at CF, but it humbles me to see someone write CF that parses CF and finds problems.