Thursday 12 July 2012

CFML: onApplicationStart is not intrinsically single-threaded

G'day:
I read somewhere today someone say onApplicationStart is single-threaded, as a justification for removing a <cflock> from some code that was causing another person some problems.

In the explicit context under discussion the person was offering good advice, but the blanket statement that onApplicationStart is single-threaded is not entirely accurate.

In ColdFusion, application-start-up is single-threaded. And during application start-up onApplicationStart is called. So that call to onApplicationStart is also single-threaded.

However the single-threadedness here is not intrinsically related to onApplicationStart.


Consider this code:

<!--- Application.cfc --->
<cfcomponent>

    <cfset this.name = "singleThreadedAppStart1">

    <cffunction name="onApplicationStart">
        <cflog file="#this.name#" text="Top of onApplicationStart called via #CGI.script_name#">
        <cfset sleep(10000)>
        <cflog file="#this.name#" text="Bottom of onApplicationStart called via #CGI.script_name#">
    </cffunction>

</cfcomponent>


<!--- firstRequest.cfm --->
<cfset msg = "I was the first request, run by #getCurrentTemplatePath()#">
<cfoutput>#msg#</cfoutput>


<!--- secondRequest.cfm --->
<cfset msg = "I was the second request, run by #getCurrentTemplatePath()#">
<cfoutput>#msg#</cfoutput> 

Now...if I start the ColdFusion instance up afresh and run firstRequest.cfm and then wait 5sec then run secondRequest.cfm what gets logged? Well application startup is single-threaded, so firstRequest.cfm dives in and starts running onApplicationStart, and then secondRequest.cfm queues up until app start-up is finished, and then it continues. So in the log we get this:

Top of onApplicationStart called via /cf/cfcs/app/singleThreadedAppStart/firstRequest.cfm
Bottom of onApplicationStart called via /cf/cfcs/app/singleThreadedAppStart/firstRequest.cfm

This is what we expect: only the first request caused the app to start-up whilst the second one waited for app start-up to finish before continuing. So only firstRequest.cfm's request gets logged.

This is not at all surprising.

However what about if we use a common technique to "restart" an application: calling onApplicationStart explicitly? Consider this code:

<!--- firstReset.cfm --->
<cfset createObject("Application").onApplicationStart()>
<cfset msg = "I was the first reset call, run by #getCurrentTemplatePath()#">
<cfoutput>#msg#</cfoutput>

<!--- secondReset.cfm --->
<cfset createObject("Application").onApplicationStart()>
<cfset msg = "I was the second reset call, run by #getCurrentTemplatePath()#">
<cfoutput>#msg#</cfoutput>

Again we restart the ColdFusion instance, then first call firstReset.cfm, wait 5sec then call secondReset.cfm. What's in the log now?

Top of onApplicationStart called via /cf/cfcs/app/singleThreadedAppStart/firstReset.cfm
Top of onApplicationStart called via /cf/cfcs/app/singleThreadedAppStart/secondReset.cfm
Bottom of onApplicationStart called via /cf/cfcs/app/singleThreadedAppStart/firstReset.cfm
Bottom of onApplicationStart called via /cf/cfcs/app/singleThreadedAppStart/secondReset.cfm

So there's no single-threading going on here at all: both requests are running the code simultaneously.  Again, this is because it's not onApplicationStart that's intrinsically single-threaded, it's the ColdFusion app start-up process that's single-threaded.

This is a minor distinction that won't impact the bulk of situations, but it's important to remember.

UPDATE.  I should have cross referenced back to the docs for Application.cfc's event handlers in my original draft.

--
Adam