Adobe ColdFusion 8

Nesting locks and avoiding deadlocks

Inconsistent nesting of cflock tags and inconsistent naming of locks can cause deadlocks (blocked code). If you are nesting locks, you must consistently nest cflock tags in the same order and use consistent lock scopes (or names).

A deadlock is a state in which no request can execute the locked section of the page. All requests to the protected section of the page are blocked until there is a time-out. The following table shows one scenario that would cause a deadlock:

User 1

User 2

Locks the Session scope.

Locks the Application scope.

Tries to lock the Application scope, but the Application scope is already locked by User 2.

Tries to lock the Session scope, but the Session scope is already locked by User 1.

Neither user's request can proceed, because it is waiting for the other to complete. The two are deadlocked.

Once a deadlock occurs, neither of the users can do anything to break the deadlock, because the execution of their requests is blocked until the deadlock is resolved by a lock time-out.

You can also cause deadlocks if you nest locks of different types. An example of this is nesting an exclusive lock inside a read-only lock of the same scope or same name.

In order to avoid a deadlock, lock code sections in a well-specified order, and name the locks consistently. In particular, if you need to lock access to the Server, Application, and Session scopes, you must do so in the following order:

  1. Lock the Session scope. In the cflock tag, specify scope="Session".
  2. Lock the Application scope. In the cflock tag, specify scope="Application".
  3. Lock the Server scope. In the cflock tag, specify scope="Server".
  4. Unlock the Server scope.
  5. Unlock the Application scope.
  6. Unlock the Session scope.

Note: You can skip any pair of lock and unlock steps in the preceding list if you do not need to lock a particular scope. For example, you can omit steps 3 and 4 if you do not need to lock the Server scope.

Copying shared variables into the Request scope

You can avoid locking some shared-scope variables multiple times during a request by doing the following:

  1. Copy the shared-scope variables into the Request scope in code with an exclusive lock in the Application.cfc onRequestStart method or the Application.cfm page.
  2. Use the Request scope variables on your ColdFusion pages for the duration of the request.
  3. Copy the variables back to the shared scope in code with an exclusive lock in the Application.cfc onRequestEnd method on the OnRequestEnd.cfm page.

With this technique the "last request wins." For example, if two requests run simultaneously, and both requests change the values of data that was copied from the shared scope, the data from the last request to finish is saved in the shared scope, and the data from the previous request is not saved.

Locking application variables efficiently

The need to lock application variables can reduce server performance, because all requests that use Application scope variables must wait on a single lock. This issue is a problem even for write-once read-many variables, because you still must ensure the variable exists, and possibly set the value before you can read it.

You can minimize this problem by using a technique such as the following to test for the existence of application variables and set them if they do not exist:

  1. Use an Application scope flag variable to indicate if the variable or variables are initialized. In a read-only lock, check for the existence of the flag, and assign the result to a local variable.
  2. Outside the cflock bock, test the value of the local variable
  3. If it the local variable indicates that the application variables are not initialized, get an exclusive Application scope lock.
  4. Inside the lock, again test the Application scope flag, to make sure another page has not set the variables between step one and step four.
  5. If the variables are still not set, set them and set the Application scope flag to true.
  6. Release the exclusive lock.

The following code shows this technique:

<!--- Initialize local flag to false. --->
<cfset app_is_initialized = False>
<!--- Get a readonly lock --->
<cflock scope="application" type="readonly">
    <!--- read init flag and store it in local variable --->
    <cfset app_is_initialized = IsDefined("APPLICATION.initialized")>
</cflock>
<!--- Check the local flag --->
<cfif not app_is_initialized >
<!--- Not initialized yet, get exclusive lock to write scope --->
    <cflock scope="application" type="exclusive">
        <!--- Check nonlocal flag since multiple requests could get to the
                exclusive lock --->
        <cfif not IsDefined("APPLICATION.initialized") >
            <!--- Do initializations --->
            <cfset APPLICATION.varible1 = someValue >
             ... 
            <!--- Set the Application scope initialization flag --->
            <cfset APPLICATION.initialized = "yes">
        </cfif>
    </cflock>
</cfif>