Making Persistant CFCs Thread Safe

I get bit by the multithreading bug on a regular basis.  This site has had it’s fair share (and I think I’ve managed to remove them all out by now).

Generally I find that multithreading bugs mostly occur when you store CFCs in shared persistant scopes, such as application or server.

So as a rule of thumb to make sure you don’t get bit by the dreaded multithreaded bug, here are my tips and tricks:

  1. Use CFMX 6.1. It is possible to do persitatant CFCs with 6.0, but it’s really not consistant in it’s applications.
  2. var all your local variables within CFC methods.  This is my NUMBER 1 cause of multithreading issues, I forget this all the time.
  3. Use the arguments scope when referring to arguments passed through in the CFC method. (This would be my number 2).
  4. var all your local variables within CFC methods.  This is SO important, I’m going to write it twice.
  5. Refer to instance variables by some sort of scope, beit directly through the variables scope, or if you use an instance struct of some kind, use that.
  6. Use appropriate locking to ensure that data corruption doesn’t occur.  As far as I am aware, CF arrays are not thread safe, so write appropriate locking when dealing with them. I find named locks do the job nicely.

    EDIT :::: Actually, I retract this statement.  I did some investigating, and the Java array extends java.util.Vector, which IS thread safe.  To quote from the relevent javadocs "As of the Java 2 platform v1.2, this class has been retrofitted to implement List, so that it becomes a part of Java’s collection framework. Unlike the new collection implementations, Vector is synchronized.". I had originally thought that the ColdFusion array extended java.util.ArrayList, which is NOT thread safe, and requires itself to be constructed through java.utils.Collections.synchronizedList() to be thread safe. My bad.
  7. Write an error handler that sends you emails when an error occurs.  Just because you don’t see it, doesn’t mean it doesn’t exist, and multithreaded errors tend to be a sneaky little bunch.  I have a tendency to use a modified version of the DumpVar udf from Cflib that outputs strings with a XMLFormat() on them, and then dump out the error, CGI and Form structs to the email.
  8. Use some sort of stress tool (I’ve been using the MS Web Application Stress tool) and catch any and all the emails that come out of your above error handler. Fix these bugs. Rinse and Repeat.

That’s pretty much it.  That should make your CFCs thread safe.

It seems really simple, but I get caught with it so often (damn being used to declarative programming languages!) I figured I would write up a post.

Let me know if you have any other tips and tricks that help you stave off the dreaded multithreading demon.

Leave a Comment

Comments

  • Scott Barnes | July 14, 2004

    I’d also dump the errors to a file within the HDD itself, as if you deploy your application and the email isn’t allowed (ie closed off network) you need to be able to do some kind of "debug" audit on the application.

    The whole local var is a big must, and most people do forget that CFLOOP index="I" will come up as a variables.i so they need to declare var i before they run the cfloop index="" routines. Another case of CFC going apeshit in a singleton situation.

    Becareful with your Error catchments aswell, as sometimes the cfcatch comes back as an actual object and not a structure of strings… so you may have to interogate your error a little more (see how CFMX actually does it via its inbuilt templates)

    Finally
    DataLocking – Look into it, as your DB can get screwd and see how this affects your CFC
    Exception vs Error Handling, sometimes you need to throw just exceptions which aren’t bad but capture errors which are.. know the difference per context of what your are doing

  • Christian Cantrell | July 15, 2004

    Couple of comments:

    1. As Scott points out, watch out for the less obvious variables like your loop indexes, but also your query names. You must "var" your queries, just like any other variables. The only variables you don’t need to "var" are implicit variables derived from tags like CFHTTP or CFFILE.

    2. Just like ColdFusion arrays extend java.util.Vector so that they are thread safe (you are exactly right, Mark), structures, and hence your variable scopes, extend java.util.Hashtable, which is also synchronized (as opposed to HashMap, which is not), which means your structures are also thread safe. You DO NOT have to lock your structures or access to variable scopes unless you are doing so to prevent race conditions. This is a big misconception in the CF world, and I see people doing this all the time. All you’re doing is slowing down your applications unnecessarily, albeit minutely, but as a rule, synchronized code should be at as low a level as possible (in other words, let CF do the synchronization for you).

  • Christian Cantrell | July 15, 2004

    Raymond Camden just informed me of a mistake in my last comment. The statement that variables derived from tags like CFFILE and CFHTTP don’t need to scoped is not entire accurate. It’s true in the sense that they cannot be VARed, but not true in the sense that they do not need to be. In fact, they will cause your code to be thread-unsafe, and the only thing you can do about it is lock all instances of these tags. For instance, use named lock around all your CFFILE tags. Name the lock something like "CFFILE" to make it easy on yourself while you’re programming, and to make sure all the locks are named consistently (since if you misname one, no errors will be generated, but the code will not be properly locked).

    Hopefully this is something that can be addressed in an upcoming release. Thanks to Ray for the heads-up.

  • Michael J. Dyer | July 15, 2004

    I’ve gotten into the habit of doing a ‘cfset var local = structNew()’ within my routines. That way everything appears as ‘local.varname’ or ‘variables.varname’ or ‘someotherscope.varname’ and then they’re easy to tell apart. It also gets around having to declare them all as var at the top of the routine.

  • Scott Barnes | July 15, 2004

    In regardss to Christians "You DO NOT" have to lock structures, is it safe to have a Server.myArray that all can have access to? in that, if two people by some miracle go to write information to that Server.myArray will it automatically govern that process in a "que" procedure? in that Person A may have access First, but Person B will win in the end, as he was the last person to technically access that array (even though they were both accessing the array at the same time or close to).

    I ask this, as We had concerns over allowing someone to manipulate a large array, and if PersonA’s process wasn’t finished by the time PersonB goes to access that Server.Array, what then happens?

  • Mark | July 15, 2004

    Scott,

    That should be exactly what happens.

    When a object/method is ‘synchronised’ is Java, it means that only one thread can access it at one time.

    So yes, it will be queued.

    (and if it doesn’t I will eat my hat.. not that I’m wearing one)

    I think that explains synchronization reasonably well.
    http://java.sun.com/docs/books/tutorial/essential/threads/monitors.html