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 RIA Unleashed (Nov. 2010) with:

What If ColdFusion's CFThread Tag Had An Interval Attribute?

By Ben Nadel on
Tags: ColdFusion

In Javascript, there are two functions - setTimeout() and setInterval() - that allow you to perform actions in an asynchronous fashion. While setTimeout() executes only once, setInterval() will execute a given function repeatedly at the given interval. On the ColdFusion side, we have CFThread, which allows us to perform asynchronous tasks; but, unlike Javascript, the only way to create repetitive asynchronous behavior in ColdFUsion is to use something like a scheduled task. Scheduled tasks are good; but, there's something about them that I've never really liked. As such, I wondered if we could bridge the gap by giving the CFThread tag an Interval attribute?

If CFThread truly had an Interval attribute, the body of the CFThread tag would provide the behavior which would be run at the end of every interval; since this is just a proof of concept, however, we have to invoke a little hackery to make things work. In the following code, I am using a beautiful idea that Elliott Sprehn outlined in his CFUNITED 2010 talk - "I Bet You Didn't Know You Could Do That With ColdFusion." Specifically, I am referring to defining a ColdFusion user defined function (UDF) as the "body" of a ColdFusion custom tag:

  • <cf_mytag iterator="myBody">
  • <cffunction name="myBody">
  •  
  • <!--- Tag body here. --->
  •  
  • </cffunction>
  • </cf_mytag>

As you can see here, where we would normally define the custom tag body, we are, instead, defining a user defined function. Then, we are passing that UDF name (or reference if you prefer) to the custom tag such that it may manually invoke the UDF as the custom tag body (either once or in an iterative mannor). Using a UDF-as-body approach does create some complications as far as binding and execution context go; but, as you'll see in my proof of concept, these hurdles can be overcome with a little bit of hackery (different than the "Arguments" approach Elliott used in his presentation, although ironically, based on something else that Elliott taught me).

Now that you understand the use of UDFs as custom tag bodies, let's take a look at my CFThread-Interval proof of concept:

  • <!---
  • Set the counter that will be incremented by the interval callback
  • method.
  • --->
  • <cfset counter = 1 />
  •  
  •  
  • <!---
  • Start an asychronous thread and run it at intervals of 500
  • millisconds. Since I am not *truly* defining a CFThread here,
  • I need provide a function callback in leu of the CFThread tag
  • body.... yo, this is just a proof of concept! In reality, the
  • CFThread body would be THE function that executed at intervals.
  • --->
  • <cf_thread
  • name="interval"
  • action="run"
  • interval="250"
  • callback="intervalCallback">
  • <!--- --->
  • <cffunction name="intervalCallback">
  •  
  • <!--- With each interval, increment the counter. --->
  • <cfset counter++ />
  •  
  • </cffunction>
  • <!--- --->
  • </cf_thread>
  •  
  •  
  • <!---
  • Sleep the current thread - that should give our interval
  • enough time to execute several interval durations (and update
  • the counter variable).
  • --->
  • <cfthread
  • action="sleep"
  • duration="2500"
  • />
  •  
  •  
  • <!---
  • Kill our interval thread. We don't want this beast running
  • indefinitely on the machine.
  • --->
  • <cf_thread
  • name="interval"
  • action="terminate"
  • />
  •  
  •  
  • <!--- Output the current counter variable. --->
  • <cfoutput>
  •  
  • Counter: #counter#
  •  
  • </cfoutput>

To mimic the CFThread tag, I've created a thread.cfm (cf_thread) ColdFusion custom tag. As I explained above, this custom tag uses a user defined function, "intervalCallback," in leu of an authentic CFThread tag body. While this concept might seem foreign, the rest of this demo should be easier to understand: we're creating a couter in the main page which our spawned thread will then increment at 250ms intervals. After we launch our asynchronous thread, I am sleeping the primary thread for a few seconds in order to allow enough time for the "interval" thread to execute a few times.

When we run the above demo, we get the following output:

Counter: 10

As you can see, in the 2.5 seconds that the primary page went to sleep, our asynchronous thread, running at 250ms intervals, was able to execute 9 times, incrementing the counter once per execution.

Ok, so how does this cf_thread custom tag work? Typically, when you execute a user defined function (UDF), such as the one we are using for the custom tag body, the UDF executes in the context of the calling page, not the defining page. As such, we have to dip down into the Java layer to manually execute the tag-body UDF in the context of the parent page as defined by the custom tag's Caller scope. Once we can do that, the asynchronous behavior itself is simply defined as a CFThread that runs for an indefinite amount of time, sleeping for a duration defined by the custom tag's Interval attribute:

Thread.cfm (cf_thread)

  • <!---
  • NOTE: This cf_thread.cfm custom tag is not meant to be a full
  • replica of the CFThread functionality; I am only trying to create
  • functionality as a proof of concept that is related to running
  • threads on an interval.
  • --->
  •  
  •  
  • <!--- Param the tag attributes. --->
  •  
  • <cfparam
  • name="attributes.name"
  • type="variablename"
  • />
  •  
  • <cfparam
  • name="attributes.action"
  • type="string"
  • default="run"
  • />
  •  
  • <!---
  • We only need the callback name if the action is run - we will
  • assume that the interval is non-zero, but we won't be validating
  • for that in this proof of concept.
  • --->
  • <cfif (attributes.action eq "run")>
  •  
  • <cfparam
  • name="attributes.interval"
  • type="numeric"
  • />
  •  
  • <cfparam
  • name="attributes.callback"
  • type="variablename"
  • />
  •  
  • </cfif>
  •  
  •  
  • <!--- ------------------------------------------------- --->
  • <!--- ------------------------------------------------- --->
  •  
  •  
  • <!--- Check to see if the action is terminate. --->
  • <cfif (attributes.action eq "terminate")>
  •  
  • <!--- Kill the given thread. --->
  • <cfthread
  • name="#attributes.name#"
  • action="terminate"
  • />
  •  
  • <!---
  • Exist out of this tag since it shouldn't do anything
  • else after terminating a thread.
  • --->
  • <cfexit method="exitTag" />
  •  
  • </cfif>
  •  
  •  
  • <!--- ------------------------------------------------- --->
  • <!--- ------------------------------------------------- --->
  •  
  •  
  • <!---
  • If we've made it this far, the only other action that we are
  • currently supporting in RUN with an interval. In order to get
  • this proof of concept to work, we are going to have to execute
  • the interval callback in the context of the CALLER scope. Since
  • UDFs outside of ColdFusion components bind to their execution
  • context, we have to jump through some black-magic hoops to get
  • this to execute properly.
  •  
  • NOTE: This is in heavy thanks to Elliott Sprehn who's Kung Fu
  • is truly amazing.
  • --->
  •  
  •  
  • <!---
  • Get the meta data for the caller page. This will give us the
  • CLASS used for the caller scope; from this, we can use
  • reflection to access the given field (page context) from
  • a given instance of that class (caller).
  • --->
  • <cfset callerMetaData = getMetaData( caller ) />
  •  
  • <!---
  • Get the class field that would hold a reference to the
  • pageContext. By default, we cannot access this field so we'll
  • have to toggle is access rights.
  • --->
  • <cfset contextField = callerMetaData.getDeclaredField( "pageContext" ) />
  •  
  • <!---
  • This is a private field so we have to explicitly change its
  • access (I don't fully understand things at this level - this
  • is just what Elliott had... and, if you don't do it, it tells
  • you that the field is private and throws an error).
  • --->
  • <cfset contextField.setAccessible( true ) />
  •  
  • <!---
  • Now that we have the Field object that represents the
  • PageContext property, use reflection to get that property from
  • the CALLER instance.
  • --->
  • <cfset callerPageContext = contextField.get( caller ) />
  •  
  • <!---
  • This method will simply echo whatever arguments have
  • been passed to it. This is just an easy way to define an
  • argument collection to be used when invoking the passed-in
  • Greeting method.
  •  
  • NOTE: To be used with NAMED attributes. Positional attributes
  • have be executed using an Object[] array... (I think).
  • --->
  • <cffunction
  • name="getArgumentCollection"
  • access="public"
  • output="false"
  • hint="I echo the arguments collection.">
  •  
  • <cfreturn arguments />
  • </cffunction>
  •  
  •  
  •  
  • <!---
  • At this point, we should have everything we need in order to
  • execute the interval callback function in the context of the
  • caller scope. Now, we have to start the CFThread that actually
  • runs the interval function. You'll notice that we are NOT going
  • to pass in any of the function stuff. Passing data to CFThread
  • via its tag attributes performes deep-copy on the values; we
  • need those refrences to be maintained.
  • --->
  • <cfthread
  • name="#attributes.name#"
  • action="run">
  •  
  • <!---
  • Since this thread is meant to run at an interval, we need
  • to enter an indefinite loop - remember, this is just a proof
  • of concept.
  • --->
  • <cfloop condition="true">
  •  
  • <!---
  • Sleep the tag for the interval. Notice that we are
  • accessing this via the custom tag's Variables scope, not
  • the attributes. If we tried to use the Attributes scope,
  • it would have been conflicting with the CFThread's local
  • attributes collection.
  • --->
  • <cfthread
  • action="sleep"
  • duration="#variables.attributes.interval#"
  • />
  •  
  • <!---
  • Invoke the callback function in the caller context.
  •  
  • NOTE: To invoke the method, we are using the underlying,
  • undocumented Java methods for invoking ColdFusion methods.
  • --->
  • <cfset caller[ variables.attributes.callback ].invoke(
  • caller.variables,
  • javaCast( "string", variables.attributes.callback ),
  • callerPageContext.getPage(),
  • getArgumentCollection()
  • ) />
  •  
  • </cfloop>
  •  
  • </cfthread>
  •  
  •  
  • <!---
  • NOTE: Since ColdFusion moves all UDF definitions to the top
  • of a file when it compiles (for lack of a better metatphor),
  • we don't need to worry about the END MODE of execution for this
  • tag - the callback UDF, while defined inside the custom tag
  • body, is actually available before the custom tag executes.
  • --->
  • <cfexit method="exitTag" />

The bulk of this custom tag is concerned with getting the Caller scope's PageContext in order to invoke our UDF in the appropriate execution context. If you look past that, however, you'll see that there is little more to this custom tag than an encapsulated CFThread tag that uses a Sleep directive to pause the asynchronous thread for each interval. I assume that this ties up a valuable thread on the server; but, like I said, this is just a proof of concept.

When it comes to passing data into a ColdFusion CFThread, you have to be very careful; data passed to a new thread is passed by deep-copy. Since we need to maintain all of the referential integrity between the caller context and the custom tag body, you'll notice that I am not passing any data into the custom tag. Rather, I am relying on the implicit sharing of the custom tag's Variables scope with the CFThread execution context.

I know that you might be looking at this and thinking that using Scheduled Tasks is just going to be easier; but, I think there is something really nice about creating repetitive behavior within the context of the relevant code. Imagine that we had a ColdFusion component instance that needed to do something internal to the CFC on a regular basis (ex. cleaning cache, watching target directory, etc.); with a CFThread/Interval situation, we could simply launch a "timer" within the component and keep all of the logic completely encapsulated. If we had to go the scheduled task route, however, not only would we have to provide an unrelated landing page for the scheduled task, we'd also have to grant external access to a private piece of behavior.

CFThread is a super powerful feature of ColdFusion and I think building in some Interval behavior would make it all the more awesome. Of course, it might be totally misguided of me to try and pull features relevant in Javascript up into a server-side technology?




Reader Comments

Note that JavaScript setInterval and setTimeout do not execute code async. They defer execution of the code, but the code is nevertheless executed within the ui thread (or, in a WebWorker, in the WebWorker thread).

Reply to this Comment

@Justice,

Ah, very interesting. I had never actually thought about what thread is used once the execution of the callback actually takes place. Since you bring up WebWorkers, however, I believe they are truly asynchronous, correct?

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.