Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at Scotch On The Rock (SOTR) 2010 (London) with:

Creating Thread-Safe Components With OnMissingMethod()

Posted by Ben Nadel
Tags: ColdFusion

NOTE: Elliott Sprehn has pointed out that what I discuss here is a misuse of terminology and the idea of creating something Thread-Safe is not like what I have described. Please take this post purely as an experiment with OnMissingMethod() and nothing more.

This was just some fooling around that I did, but thought I would share it. Most of the time, I have components that don't need to be thread safe because they are only used in a non-persisting scope such as REQUEST or (page) VARIABLES. Such an example of this would be a message queue component that encapsulates an array of items and has utility methods for returning collections and iterators. For a single page, you don't have to worry about single-threading certain method calls; however, if that same component were used to hold system messages across page calls, it might become more important to make sure individual methods were thread-safe.

To create a base case for this post, here is a ColdFusion component, MessageQueue.cfc, that simply contains an internal array to which you can add messages:

  • <cfcomponent
  • output="false"
  • hint="Holds a queue of messages.">
  •  
  • <!---
  • Run pesudo code to set up default sturctures
  • and data values.
  • --->
  • <cfset VARIABLES.Instance = {} />
  • <cfset VARIABLES.Instance.Queue = [] />
  •  
  •  
  • <cffunction
  • name="Init"
  • access="public"
  • returntype="any"
  • output="false"
  • hint="Returns an intialized component.">
  •  
  • <!--- Return This reference. --->
  • <cfreturn THIS />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="Add"
  • access="public"
  • returntype="any"
  • output="false"
  • hint="Adds a message to the internal queue and then returns This reference for method chaining.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="Message"
  • type="string"
  • required="true"
  • hint="The queue to add to the message."
  • />
  •  
  • <!--- Add the message to the queue. --->
  • <cfset ArrayAppend(
  • VARIABLES.Instance.Queue,
  • ARGUMENTS.Message
  • ) />
  •  
  • <!--- Return This reference. --->
  • <cfreturn THIS />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="GetMessages"
  • access="public"
  • returntype="array"
  • output="false"
  • hint="Returns a copy of the internal message queue.">
  •  
  • <cfreturn VARIABLES.Instance.Queue />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="Size"
  • access="public"
  • returntype="numeric"
  • output="false"
  • hint="Returns the size of the internal message queue.">
  •  
  • <cfreturn ArrayLen( VARIABLES.Instance.Queue ) />
  • </cffunction>
  •  
  • </cfcomponent>

Not a whole lot going on - one method with arguments and two methods that do not have any arguments. To use this, we could simply do something like this:

  • <!--- Create a message queue. --->
  • <cfset objMessages = CreateObject(
  • "component",
  • "MessageQueue"
  • ).init() />
  •  
  •  
  • <!--- Add some messages to it. --->
  • <cfset objMessages
  • .Add( "Kim, you're such a hottie!" )
  • .Add( Message = "Suzie, you're smile brightens my day" )
  • .Add( "Anna Banana, why you so cool?!?" )
  • />
  •  
  • <!--- Check to see if there are messages. --->
  • <cfif objMessages.Size()>
  •  
  • <!--- Get the messages. --->
  • <cfloop
  • index="strMessage"
  • array="#objMessages.GetMessages()#">
  •  
  • <p>
  • #strMessage#
  • </p>
  •  
  • </cfloop>
  •  
  • </cfif>

Here, we are creating the MessageQueue.cfc instance, adding some messages to it, and then outputting those messages. I am using a mix of both named an ordered arguments when I add the messages as this will come into play later on. When we run this, the three messages we added are echoed out.

This example all took place in the span of a single page request; however, if our MessageQueue.cfc was stored in the SESSION, we would want to make it thread safe. So, how to do we create a thread safe version of this ColdFusion component? Well, how do you make anything thread safe in ColdFusion? Add some locking action via ColdFusion's CFLock tag. And, since this is very action-specific (in our example), we want to be using named locks, not scope locks (I would recommend always using named locks).

One way to create a thread-safe versions of this ColdFusion component would be to create a shell component that extends the MessageQueue.cfc and adds overrides all methods with thread-safe methods. To do this, the shell ColdFusion component would implement a CFLock tag in each of its overriding methods and then turn around and call the same method on its SUPER counterpart:

  • <cfcomponent
  • output="false"
  • extends="MessageQueue"
  • hint="Holds a thread safe queue of messages.">
  •  
  • <!---
  • Run pesudo code to set up default sturctures
  • and data values.
  • --->
  • <cfset VARIABLES.Instance.LockID = CreateUUID() />
  •  
  •  
  • <cffunction
  • name="Add"
  • access="public"
  • returntype="any"
  • output="false"
  • hint="Adds a message to the internal queue and then returns This reference for method chaining.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="Message"
  • type="string"
  • required="true"
  • hint="The queue to add to the message."
  • />
  •  
  • <!--- Lock method call. --->
  • <cflock
  • name="#VARIABLES.Instance.LockID#-Message"
  • type="exclusive"
  • timeout="5">
  •  
  • <cfreturn SUPER.Add(
  • Message = ARGUMENTS.Message
  • ) />
  •  
  • </cflock>
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="GetMessages"
  • access="public"
  • returntype="array"
  • output="false"
  • hint="Returns a copy of the internal message queue.">
  •  
  • <!--- Lock method call. --->
  • <cflock
  • name="#VARIABLES.Instance.LockID#-GetMessages"
  • type="exclusive"
  • timeout="5">
  •  
  • <cfreturn SUPER.GetMessages() />
  •  
  • </cflock>
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="Size"
  • access="public"
  • returntype="numeric"
  • output="false"
  • hint="Returns the size of the internal message queue.">
  •  
  • <!--- Lock method call. --->
  • <cflock
  • name="#VARIABLES.Instance.LockID#-Size"
  • type="exclusive"
  • timeout="5">
  •  
  • <cfreturn SUPER.Size() />
  •  
  • </cflock>
  • </cffunction>
  •  
  • </cfcomponent>

Notice here that in MessageQueueSafe.cfc, we have all the same methods as the base component, MessageQueue.cfc. This is necessary because the two CFCs need to have the same interface to the external world. This thread safe component also creates a LockID which is a UUID used in the named locking. This is done to make sure these locks don't conflict with any other locks in the system or other instances of this component.

This component would then be instantiated and used in exactly the same way the non-thread safe version would be used:

  • <!--- Create a THREAD SAFE message queue. --->
  • <cfset objMessages = CreateObject(
  • "component",
  • "MessageQueueSafe"
  • ).init() />
  •  
  •  
  • <!--- Add some messages to it. --->
  • <cfset objMessages
  • .Add( "Kim, you're such a hottie!" )
  • .Add( Message = "Suzie, you're smile brightens my day" )
  • .Add( "Anna Banana, why you so cool?!?" )
  • />
  •  
  • <!--- Check to see if there are messages. --->
  • <cfif objMessages.Size()>
  •  
  • <!--- Get the messages. --->
  • <cfloop
  • index="strMessage"
  • array="#objMessages.GetMessages()#">
  •  
  • <p>
  • #strMessage#
  • </p>
  •  
  • </cfloop>
  •  
  • </cfif>

That's fairly simply, but it requires a bit of overhead. It means that for any component that needs to be made thread-safe, you have write a whole other component with all the same functions and arguments and there is basically a 1:1 effort ratio. Furthermore, if you ever make changes to the base component, you also have to make changes to the thread-safe component. This is not fun for maintenance.

So, then it occurred to me that maybe we could take the idea behind the MessageQueueSafe.cfc and use ColdFusion 8's OnMissingMethod() to create a more general solution. If we create a generic "thread-safe" component that takes a target component (such as our MessageQueue.cfc), it would use the OnMissingMethod() event handler to catch method calls, implement some CFLock action, and then execute the requested method on the composed, target component.

After a bit of fooling around with this idea, here is what I came up with:

  • <cfcomponent
  • output="false"
  • hint="Creates a more thread-safe version of any CFC.">
  •  
  • <!---
  • Run pesudo code to set up default sturctures
  • and data values.
  • --->
  • <cfset VARIABLES.Instance = {} />
  • <cfset VARIABLES.Instance.Target = "" />
  • <cfset VARIABLES.Instance.ID = CreateUUID() />
  •  
  •  
  • <cffunction
  • name="Init"
  • access="public"
  • returntype="any"
  • output="false"
  • hint="Returns an intialized thread-safe component.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="Target"
  • type="any"
  • required="true"
  • hint="The CFC intance that we want to make thread safe"
  • />
  •  
  • <!--- Store target. --->
  • <cfset VARIABLES.Instance.Target = ARGUMENTS.Target />
  •  
  • <!--- Return This reference. --->
  • <cfreturn THIS />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="OnMissingMethod"
  • access="public"
  • returntype="any"
  • output="false"
  • hint="Wraps around the target object to make the methods thread-safe.">
  •  
  • <!--- Define arguments. --->
  • <cfargument
  • name="MissingMethodName"
  • type="string"
  • required="true"
  • hint="The name of the missing method."
  • />
  •  
  • <cfargument
  • name="MissingMethodArguments"
  • type="struct"
  • required="true"
  • hint="The arguments that were passed to the missing method. This might be a named argument set or a numerically indexed set."
  • />
  •  
  • <!--- Define the local scope. --->
  • <cfset var LOCAL = {} />
  •  
  •  
  • <!---
  • Lock this method call. We are trying to make this
  • method thread safe so give it a named lock that is
  • a combination of the CFC ID and the method name.
  • --->
  • <cflock
  • name="#VARIABLES.Instance.ID#-#ARGUMENTS.MissingMethodName#"
  • type="exclusive"
  • timeout="5">
  •  
  •  
  • <!--- Get the array of argument keys. --->
  • <cfset LOCAL.Keys = StructKeyArray(
  • ARGUMENTS.MissingMethodArguments
  • ) />
  •  
  •  
  • <!---
  • Check to see if we have numeric argument keys
  • (indicating that we have orderd arguments, NOT
  • named arguments).
  • --->
  • <cfif (
  • ArrayLen( LOCAL.Keys ) AND
  • IsNumeric( LOCAL.Keys[ 1 ] )
  • )>
  •  
  • <!---
  • With ordered arguments, we have to build up a
  • dynamic evaluation string (this is lame, but
  • I cannot find anyway to get around this).
  • --->
  • <cfset LOCAL.Args = "" />
  •  
  • <!--- Loop over keys. --->
  • <cfloop
  • index="LOCAL.Index"
  • array="#LOCAL.Keys#">
  •  
  • <!--- Get the argument value. --->
  • <cfset LOCAL.Value = ARGUMENTS.MissingMethodArguments[ LOCAL.Index ] />
  •  
  • <!---
  • Check to see if the value is a simple or
  • complex value. All simple values will be
  • quoted.
  • --->
  • <cfif IsSimpleValue( LOCAL.Value )>
  •  
  • <!--- Quote the argument. --->
  • <cfset LOCAL.Args &= (
  • ",""" &
  • LOCAL.Value &
  • """"
  • ) />
  •  
  • <cfelse>
  •  
  • <!---
  • Send the complex value through without
  • any quoting.
  • --->
  • <cfset LOCAL.Args &= (
  • "," &
  • LOCAL.Value
  • ) />
  •  
  • </cfif>
  •  
  • </cfloop>
  •  
  •  
  • <!--- Remove the leading comma. --->
  • <cfset LOCAL.Args = REReplace(
  • LOCAL.Args,
  • "^,",
  • "",
  • "one"
  • ) />
  •  
  • <!--- Excute the dynamic method. --->
  • <cfset LOCAL.Return = Evaluate(
  • "VARIABLES.Instance.Target." &
  • ARGUMENTS.MissingMethodName & "(" &
  • LOCAL.Args &
  • ")"
  • ) />
  •  
  • <!---
  • If the method doesn't have ordered arguments,
  • then we can simply pass the missing method
  • arguments through as the arguments collection.
  • This will be fine even if no arguments were
  • passed through.
  • --->
  • <cfelse>
  •  
  • <!---
  • With named arguments, we can simply pass the
  • missing method arguments directly onto the
  • target instance using the argument colleciton.
  • --->
  • <cfinvoke
  • component="#VARIABLES.Instance.Target#"
  • method="#ARGUMENTS.MissingMethodName#"
  • argumentcollection="#ARGUMENTS.MissingMethodArguments#"
  • returnvariable="LOCAL.Return"
  • />
  •  
  • </cfif>
  •  
  •  
  • <!--- Return the resultant value. --->
  • <cfreturn LOCAL.Return />
  •  
  •  
  • </cflock>
  • </cffunction>
  •  
  • </cfcomponent>

Notice that this ColdFusion component has only two methods: Init() and OnMissingMethod(). It has a target variable to hold the component that we wish to act on, and just as with our MessageQueueSafe.cfc, our generic ThreadSafe.cfc has a UUID to be used in named locking.

Now, when using the OnMissingMethod() event handler, there are really three scenarios you have to deal with:

  1. No arguments
  2. Named arguments
  3. Ordered arguments

Because functions in ColdFusion can be invoked using the ArgumentCollection key (or attribute if you use the CFInvoke tag), no-arguments and named-arguments can be handled in the same way. A "no arguments" scenario can be thought of as passing in an empty ArgumentCollection structure and it's very easy for OnMissingMethod() to just pass its arguments in to a CFInvoke tag. Things get a bit hairy when we get to ordered arguments. ColdFusion is quite limited when it comes to programmatically invoking a method with ordered arguments. As such, I have to use the Evaluate() method to dynamically execute the ordered arguments code.

BOLD STATEMENT OF THE DAY: If you use Evaluate() in your code, then either you don't fully understand how ColdFusion can be used OR you are trying to perform a hack or short cut that could be done in a more explicit way without the use of Evaluate().

I'm not saying Evaluate() is "bad"; I have to use it in this example, there's no way around it (that I know of). I am just saying that we should all admit that when we use Evaluate(), we are trying to cut corners and take short cuts.

That being said, when using the generic ThreadSafe.cfc ColdFusion component, you just have to add the wrapping step to the base component you would have already been using:

  • <!---
  • Create a thread-safe message queue by wrapping our
  • instance in the ThreadSafe.cfc.
  • --->
  • <cfset objMessages = CreateObject(
  • "component",
  • "ThreadSafe"
  • ).Init(
  •  
  • <!---
  • Pass an initialized MessageQueue CFC to the
  • thread safe"ifyer".
  • --->
  • CreateObject(
  • "component",
  • "MessageQueue"
  • ).init()
  •  
  • )
  • />
  •  
  •  
  • <!--- Add some messages to it. --->
  • <cfset objMessages
  • .Add( "Kim, you're such a hottie!" )
  • .Add( Message = "Suzie, you're smile brightens my day" )
  • .Add( "Anna Banana, why you so cool?!?" )
  • />
  •  
  • <!--- Check to see if there are messages. --->
  • <cfif objMessages.Size()>
  •  
  • <!--- Get the messages. --->
  • <cfloop
  • index="strMessage"
  • array="#objMessages.GetMessages()#">
  •  
  • <p>
  • #strMessage#
  • </p>
  •  
  • </cfloop>
  •  
  • </cfif>

Notice that we are creating a fully initialized MessageQueue.cfc and then passing it as our only argument to the ThreadSafe.cfc component. Once this is done, we can use the ThreadSafe.cfc instance just as we would have used MessageQueue.cfc for method invoking (we cannot introspect it at all). Also notice that this works fine with both the named and ordered arguments of the various Add() method executions.

Now that all methods will be called on the ThreadSafe.cfc, which implements named locking around its internal method calls, we know that even multiple page requests that try to invoke the same method at the same time will not lead to any race conditions. And, just to demonstrate that this works, running the above code gives us the following output:

Kim, you're such a hottie!

Suzie, you're smile brightens my day

Anna Banana, why you so cool?!?

This was just an experiment to see how we might think of utilizing ColdFusion 8's OnMissingMethod() event handler. It is, however, not something that I recommend doing. It has the benefit of being generic, meaning, this ThreadSafe.cfc can be used on any target component, but it has down sides:

  1. It uses Evaluate() in order to execute ordered arguments method invocation (this is really not good and makes my skin crawl a bit).
  2. Type checking will fail if the component has to be passed as an argument (which would NOT be the case in the MessageQueueSafe.cfc example that uses inheritance).
  3. The Output attribute of our ThreadSafe.cfc component is set to "false", but this might be different from the composed, target component. This could cause unexpected results.

I probably will never use this, but I had fun looking into it. Hopefully, you find it at least interesting.



Reader Comments

The queue isn't really thread safe at all. Access to it is synchronized, but nothing prevents the rug from getting pulled out from under you in calling code.

It's very misleading to say this Queue is thread safe, since <cfif Size() eq ArrayLen(GetMessages())> will work sometimes and not others.

Anyway, every function you have in there is atomic so adding these cflocks gains you nothing. If two threads call Add() at the same time, which ever one gets the lock, will insert first... the same would be true if there was no lock at all. Size() will behave the same. Your locking just makes already synchronized access much slower. :P

It's a novel idea though. For a more complicated object this allows simple synchronized access (similar to the Java Collections.synchronizedXXX() methods). You just need to be careful not to confuse synchronized access with thread safe.

Reply to this Comment

@Elliott,

Ok, so obviously I don't know the difference between thread-safe and synchronized :) You hit the nail on the head - when I wrote this, I think I was thinking about the collections synchronization; I have never used that, but I think that's the idea somewhere in my ramblings.

I guess, I am not sure what Thread safe even is? I don't even know how you could make something thread safe then? You'd have to lock the entire access to a page to bottle-neck it?

As far as the example itself, it was trying to keep it extremely simple so demonstrate the concept I had, no so much the actual use-case. That was my bad. However seeing as it is extremely rare that I lock anything, I was strapped to come up with something good.

I will put a note at the top of the blog that the information is not accurate. It was just some experimentations and I don't want anyone looking at this as fact.

Thanks for pointing out the gaps and my misuse of terminology.

Reply to this Comment

Perhaps Elliott could explain the difference between synchronized and
thread-safe? I thought they were synonymous. Apparently, so did the authors of the Java 1.4 API docs for the Collections object:

http://java.sun.com/j2se/1.4.2/docs/api/java/util/Collections.html#synchronizedCollection(java.util.Collection)

The documentation of the static method synchronizedCollection() begins:
"Returns a synchronized (thread-safe) collection backed by the specified collection."

Reply to this Comment

I apologize that I misspoke and wasn't quite clear. The queue is inherently internally thread safe because of how CF implements arrays, but calling code gets no guarantee, and synchronizing access doesn't change that.

@Ben

Internally ColdFusion implements arrays using java.util.Vector which is already synchronized at the method level. So we are guaranteed that the Add(), Size() and GetMessages() methods will all block when they get to the point where they'd try and access the actual array you have, and only one will execute at a time. As there are no external calculations made in your code that are based on the temporal state of the queue, it's safe to allow any thread to call methods on this object since no error condition will ever happen due to inconsistent state related to multiple threads accessing the instance.

This would be like a function that assigns a value to a string from an argument.

function assign( value ) {
variables.value = value;
}

There's no threading issues here, and no reason to lock. If we lock the method call, we ensure only one caller can execute this function at a time, but that doesn't solve anything, since no part of the function actually accesses a part of the object that could be in a state of change.

The below on the other hand would definitely need synchronization:

function stuff() {
variables.value = variables.value & uCase(variables.value);
}

What happens if assign() is called right after the uCase() happens? We could end up with "fooBar" when it should have been "fooFoo".

@David/Ben

These pages have a nice discussion about the issues:
http://www.ibm.com/developerworks/java/library/j-jtp09263.html
http://rayfd.wordpress.com/2007/11/11/when-a-synchronized-class-isnt-threadsafe/

The issue is that synchronizing access doesn't necessarily mean the object is thread safe at all. It might be, it might not.

An object may be internally thread safe (as imposed by synchronization) but usage might not be, or it may depend on something that could change. Ben's statements about placing this in the session scope are prime example of this.

Take for example a max-heap.

session.heap = createObject("component","Heap").init(10,5,4);
[...]
session.heap.insert( session.heap.getRoot()+1 );
writeOutput( session.heap.getRoot() );

Now from the code right here you'd say this prints out 11 right? So we'll follow Ben's statement that adding synchronization to the Heap would make it thread safe so you could store it in the session scope.

session.heap = createObject("component","ThreadSafe").init(createObject("component","Heap").init(10,5,4));
[...]
session.heap.insert( session.heap.getRoot()+1 );
writeOutput( session.heap.getRoot() );

And now we make the same statement. The code prints 11. But it doesn't have to! What if another thread calls insert and inserts 55 right before our insert? That'd get us 56 being inserted and printed. What if that happened right after the call to getRoot() in the insert()? That'd print 55.

Now what about an object that has a data provider?

provider = createObject("component","DataProvider").init(1,2,3);
[...]
foo = createObject("component","ThreadSafe").init(createObject("component","Widget").init(provider));

foo.doStuff();

Using the same logic that synchronized access means thread safe this code seems fine, but it's *not*. What happens if another thread calls DataProvider.remove()
while a method is iterating over it inside Widget? Bad things.

And there lies what I was saying. Just because you are synchronizing access to the object doesn't make using it thread safe! And worse, telling people that the result of Collections.synchronizedXXX() is thread safe is very dangerous.

Lots of code has subtle race conditions in it because of the above assumption. What's returned is internally thread safe... sometimes, and you're guaranteed that the object won't explode if two threads call its methods at the same time... sometimes, but absolutely nothing says that accessing the object in a mutli-threaded environment (ex. session scope) won't blow up in your face.

Multithreading is a pretty complex issue.

Reply to this Comment

@Elliott,

Wow - that is one cogent and well-sourced response! The links you sent portray concurrency as a major blind spot for Java developers. Even those working with multithreaded programming way up in the ColdFusion abstraction layer would do well to proceed with caution.

Thanks for taking the time to explain so clearly!

Reply to this Comment

@Elliott,

Thanks for the super thorough explanation. And again, I did not mean to try and mislead anyone - it was all for experimentation's sake, so thanks for point all this out.

As far a thread-safe goes, I just always assumed that would be handled by CFLock's at the controller level or wherever the implementing code was. I look at it like a CFTransaction:

The CFTransaction is like "thread-safe" stuff, depending on the isolation level (bear with me here), and then the individual access to the data tables has controls at the SQL server level.

That's not a great explanation or analogy, but I was trying to say thread safeness would be implemented by the programmer and synchronization would be implemented by the individual objects in use.

So much more to learn :)

Reply to this Comment

@Ben

Of course. I know you weren't trying to mislead anyone, and I definitely wasn't trying to blast you. I apologize if I misunderstood anything you were saying.

btw, your blog is awesome. You have such dedication, and an awesome attitude towards learning. It's inspirational and really a pleasure to read. Thanks! :)

Reply to this Comment

@Elliott,

With your use of smiley faces, I was quite sure that you were not trying to blast me ;) If anything, I was just a bit embarrassed to have been so off on my terminology.

Reply to this Comment

Post A Comment

?
You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.