Locking Code to Prevent Errors

Contact Us or call 1-877-932-8228
Locking Code to Prevent Errors

Locking Code to Prevent Errors

ColdFusion is a multi-threaded application, which means that it can process many requests at the same time. In some case, two requests may be trying to access the same memory space or the same file. Any code that lends itself to this possibility should be locked. To understand why, you need to understand the concept of a race condition.

Race Conditions

Race conditions occur when multiple requests attempt to modify the same data at the same moment. In Macromedia ColdFusion MX 7 Web Application Construction Kit, the authors use the following example:

<cfset APPLICATION.hitCount = APPLICATION.hitCount + 1>

In this one line of code, APPLICATION.hitCount is being read and modified. Although this happens very quickly, it is possible that two simultaneous requests read the value at the exact same time before either of them has the chance to modify it. Hence, they each read the same value (e.g, 100). They both then add 1 to the value and come up with the same number and set the new hitCount to 101. But since there were actually two separate hits, the new hitCount should be 102.

Although hit counts are not usually mission critical, this does illustrate the problem. The solution is to wrap the code in an exclusive lock:

<cflock type="Exclusive" scope="application" timeout="10"> <cfset APPLICATION.hitCount = APPLICATION.hitCount + 1> </cflock>

This forces single-threaded access to this code, which will prevent a race condition from occurring. We used an Exclusive lock, because the memory variable is being modified. In cases where the variable is only being read, you could use a ReadOnly lock:

<cflock type="ReadOnly" scope="application" timeout="10"> <cfoutput>Page hits: #APPLICATION.hitCount#</cfoutput> </cflock>

Named Locks

The examples above used scoped locks, which lock the whole application scope until the code inside the lock has been processed. While this is certainly safe, it can be overkill. To see how, take a look at the following code:

//In the onRequestStart method of Application.cfc <cflock type="Exclusive" scope="application" timeout="10"> <cfset APPLICATION.hitCount = APPLICATION.hitCount + 1> </cflock> //In the onSessionStart method of Application.cfc <cflock type="Exclusive" scope="application" timeout="10"> <cfset APPLICATION.sessionCount = APPLICATION.sessionCount + 1> </cflock>

While it's possible that these two pieces of code might be accessed at the exact same time by multiple users, this won't cause any conflict. Yet the way it is written, each request will have to cue up behind the next. Using named locks provides more granularity:

//In the onRequestStart method of Application.cfc <cflock type="Exclusive" name="HitCountLock" timeout="10"> <cfset APPLICATION.hitCount = APPLICATION.hitCount + 1> </cflock> //In the onSessionStart method of Application.cfc <cflock type="Exclusive" name="SessionCountLock" timeout="10"> <cfset APPLICATION.sessionCount = APPLICATION.sessionCount + 1> </cflock>

This way, requests only get cued up behind locks of the same name. A couple of warnings:

  • Be very careful to spell your lock names the same! Typos are very dangerous.
  • Lock names are server-wide, so if you are running multiple applications on the same server, you will want to include the application's name in the lock name (e.g, name="#APPLICATION.applicationName#SessionCountLock").
Next