Cliff Meyers wrote:

Is that locking implemented inside the cffunction tag for the method (A),
or around the call to the method itself (B)?

A:
cffunction
  cflock
    ...
  /cflock
/cffunction

B:
cflock
  myObj.myMethod(param1, param2...)
/cflock

I'm guessing you're going to say "B" since the Mach-II framework has
several CFLOCKs in the mach-ii.cfm file but none in the framework CFCs
themselves. :)  I suppose to be "safe" one should probably stick to
exclusive locks since specifying a readonly lock would in effect "break"
encapsulation since you'd have to "know ahead of time" that data wasn't
being written to the instance variables of the object and only read.  Kind
of a nuanced point I suppose.
  
Actually, "A" or "B" depending on the situation.  Option "A" lets you code without worrying about forgetting to lock something since all the logic needed to run a specific is contained within the CFC.  However, you have to be a little more careful in how you write your CFC.  Also, you have better maintainability when locking from within the CFC - when and how things change, you don't have go around changin a ton of locks.

Secondly, are you sure you need to lock?  You really only have to lock for RACE conditions - like CFCs working with instance data that are stored in a persistant scope.  Check out "locking singletons for RACE conditions" in the archives dated around the 17th of March 2005.

Below is a singleton stored in the application scope that does some purging of a data struct.  FYI, totally untested quasi-code example from a while ago of locking within a CFC
<cfcomponent display="kernel">

<cffunction name="init">
   <cfargument name="lockName" required="false" default="entityLock" type="string" />
   <cfargument name="purgeDuration" required="false" default="600" type="numeric" hint="in seconds" />
   <cfscript>
      // Set instance vars
      variables.instance = structNew();
      variables.instance.entities = structNew();
      variables.instance.lockName = arguments.lockName;
      variables.instance.purgeDuration = arguments.purgeDuration;
      // run set the next purge check timestamp
      setNextPurgeCheck();
   <cfscript>
</cffunction>

<!--- There needs to be some getEntityLock method ---->

<cffunction name="SetEntityLock">
    <cfargument name="lockData" type="struct" required="Yes" />
    <cfset var bOK = false />
    <cfset purgeEntityLocks() />
    <cflock scope="# variables.instance.lockName#" type="exclusive" throwontimeout="Yes" timeout="10">
        <!--- add a struct to the "lock registry" here --->
        <cfset bOK = true />       
    </cflock>
    <cfreturn bOK />
</cffunction>

<cffunction name="deleteEntityLock">
    <cfargument name="lockData" type="struct" required="Yes" />
    <cfset var bOK = "" />
    <cfset purgeEntityLocks() />
    <cflock scope="# variables.instance.lockName#" type="exclusive" throwontimeout="Yes" timeout="10">
        <cfset doDeleteEntity(arguments) />
    </cflock>
    <cfreturn bOK />
</cffunction>

<cffunction name="purgeEntityLocks" access="public(in case you want to manually run this from outside the cfc)/private">
    <cfset var oldEntities = arrayNew(1) />
    <cfset var doPurge = FALSE />
    <cfset var currNextPurgeCheck = getNextPurgeCheck() />
    <!--- This only runs if now() has pased the next purge check timestamp - pre-check function here --->
    <cfif now() GTE currNextPurgeCheck >
        <!--- I was thinking you might need to have some sort of double-checked locking on
            the nextPurgeCheck variable in case two people hit the purgeEntityLocks at the same time
            so instead of locking down nextPurgeCheck var because it would hurt performance,
            lock down this purge function and check again within an exclusive lock just for purging
            It's probaly not a good idea to lock purge just when checking only when purging every 10 minutes
            I guess there's a few ways of doing it--->       
        <cflock scope="purgeLock" type="exclusive" throwontimeout"yes" timeout="10">
            <cfif getNextPurgeCheck() EQ currNextPurgeCheck>
                <cfset setNextPurgeCheck() />
                <cfset doPurge = TRUE />
            </cfif>
        </cflock>
        <cfif doPurge >
            <cflock scope="# variables.instance.lockName#" type="EXCLUSIVE" throwontimeout="Yes" timeout="10">
            <!--- Code to find old timestmamp and set them into the oldEntities array--->
            ...
            <!--- loop over the array --->
            <cfloop>
                <cfset doDeleteEntiry(oldEntities[i]) />
            </cfloop>
            </cflock>
        </cfif>
    </cfif>
</cffunction>

<cffunction name="doDeleteEntity" access="private" returntype="boolean">
    <!--- Since purgeEntityLocks and deleteEntityLock use the same code to delete,
        I'm thinking it's best to separate this for maintainablity --->
    <!--- Delete code here --->
    <cfreturn booleanValue />
</cffunction>

<cffunction name="setNextPurgeCheck" access="private">
    <cfset variables.instance.nextPurgeCheck = dateAdd("s", variables.instance.purgeDuration, NOW()) />
</cffucntion>
<cffunction name="getNextPurgeCheck" access="private" returntype="date">
    <cfreturn variables.instance.nextPurgeCheck>
</cffunction>

</cfcompoent>

-- 
Peter J. Farrell :: Maestro Publishing

blog	:: http://blog.maestropublishing.com
email	:: [EMAIL PROTECTED]
phone	:: 651-204-0513
----------------------------------------------------------
You are subscribed to cfcdev. To unsubscribe, send an email to [email protected] with the words 'unsubscribe cfcdev' as the subject of the email.

CFCDev is run by CFCZone (www.cfczone.org) and supported by CFXHosting (www.cfxhosting.com).

An archive of the CFCDev list is available at
www.mail-archive.com/[email protected]

Reply via email to