Creating Thread-Safe Components With OnMissingMethod()

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."

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:

No arguments

Named arguments

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:

It uses Evaluate() in order to execute ordered arguments method invocation (this is really not good and makes my skin crawl a bit).

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).

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.

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.

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 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.

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.

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.

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.

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.

I am the co-founder and lead engineer at InVision App, Inc — the world's leading prototyping,
collaboration & workflow platform. I also rock out in JavaScript and ColdFusion 24x7 and I dream about
promise resolving asynchronously.