How to tell if code is being run inside a CFTHREAD tag

One restriction I ran into today when doing some ColdFusion8 specific
performance enhancements to
Transfer
with <cfthread> is that you can't make a child thread within an already
created <cfthread>, so something like:


<cfthread action="run" name="a">
    <cfthread action="run" name="b">
    </cfthread>
</cfthread>

Will end up with lots of errors in your logs saying things like:
Error","cfthread-1","09/24/07","12:03:42",,"A: Thread B cannot be created. Child
threads are not supported."

Which is probably not what you actually want, as you would most likely like for
you code to actually execute.

I'm not a huge fan of this limitation, but I can kind of understand where the
engineers are coming from – they don't want us CF developers to shoot ourselves
in the foot with ridiculously spawning multi threaded applications.

So, in my current situation, I wasn't too fussed if I had a whole, brand new
thread, but where my code was being run, I was so far down the stack of method
calls, I have no way of knowing if the original call had come from a standard
ColdFusion thread, or if it had come from a <cfthread> create thread, and
both of which were highly possible.  However, if the execution wasn't coming from a <cfthread> created
thread, I wanted to execute my code inside a <cfthread> statement, as I
didn't care what the results where, and it would be faster for the end
user just to have the code run asynchronously.

If that just made your head hurt, I basically have a series of method calls that
look like:

A()
 - makes a <cfthread> call, and calls C()

B()
- Does some work…
- Calls C() from the current thread.

C()
- makes a <cfthread> call, and then does some work

This is an over simplification of the process (mine is far more levels deep),
but gives you an idea of the trouble I was facing – the method call could go
through A() or through B(), and at the level of C() I wasn't aware of whether or
not I had started on the base ColdFusion thread or not.

I could have done this passing a parameter all the way down the method chain to
tell it if it should fire asynchronously, but that seemed like a lot of extra
work, and would have put a level of complexity within the whole process, and any
other process that was related to this, that it didn't seem like the appropriate
course of action.

So I thought to myself – there has to be a way to find out if the current thread
is a regular, ColdFusion thread, or if it is a <cfthread> invoked
thread, and funnily enough, skipping into some Java, there is!

The first thing we have to do, is get access to the
java.lang.Thread
object:


Thread = createObject("java", "java.lang.Thread");

Now, the Thread object, has a great method
'currentThread()',
which returns the current thread that the executing code is on:


currentThread = Thread.currentThread();

What is good to know, is that Threads in Java can exist in
ThreadGroups,
which is a handy way of grouping Threads together for common operations, and
other clever things.  The nice thing here, is that ThreadGroups have a
method called
'getName()'
which returns the name of the ThreadGroup.

Strangely enough, <cfthread> created threads live inside the ThreadGroup
named 'cfthread'! So now we can compare this against the name of the current
Thread's ThreadGroup


if(currentThread.getThreadGroup().getName() eq "cfthread")
{
  //do something...
}

We can also wrap this up nicely into a quick little UDF:


<cffunction name="amInCFThread" hint="returns 'true' if the current thread is a cfthread, returns false otherwise" access="public"
returntype="boolean" output="false">
    <cfscript>
        var Thread = createObject("java",
"java.lang.Thread");

        if(Thread.currentThread().getThreadGroup().getName() eq "cfthread")
        {
            return true;
        }

        return false;
    </cfscript>
</cffunction>

This function returns 'true' if the currently executing code is inside a
<cfthread>, and 'false' if it is not.

So now, in my function C() I can have:

C()

if(amInCFThread())
{
   // run code
}
else
{
  // make <cfthread> and run code.
}


And continue on my merry way!

Pretty handy if I'm not sure if my <cfthread>'d code will end up being run by other ColdFusion created threads or not.

Leave a Comment

Comments

  • Michael Sharman | September 24, 2007

    Nice tip Mark!

  • Luis Majano | September 26, 2007

    This will come in handy for sure!!

  • Chris Blackwell | February 17, 2008

    Just ran into this issue writing a crontab like scheduler for an application. Wanted my tasks to be fired in child threads so as not to delay the main scheduler thread.

    will have to find some kind of work around, thinking maybe a url call so CF thinks its a seperate request to fire each task:)

  • Tom | March 31, 2008

    Great workaround – I know this will come in handy when working with CFTHREAD

  • Dan | April 24, 2008

    I’m writing a daemon thread that needs to launch threads. That’s as far as it goes in regards to levels.

    I’ve also met this problem. Just wondering if you could share some tips/ideas on how to overcome this issue?

    In short daemon thread calls a UDF, which in turns call the cfthread creation.

  • Mark | April 25, 2008

    Dan – short version, you can’t. It’s a pity you can’t launch threads from threads, but that’s just the way it goes at the moment.

    Long version,
    (a) Either use a scheduled task to be your daemon thread, and then launch threads from that.

    (b) Write it in Java

  • Dan | April 25, 2008

    @Mark:
    From what I’ve been told, scheduled tasks cannot be run at less than 1 minute apart? My daemon runs every 3 seconds. Can’t be more than 5 seconds.

    I ended up using a web service call to spawn the child threads.

    So far, my prototype works. I will have to secure the web service and for the amount of threads I’ll be spawning, I think this should be adequate.

  • David | July 26, 2008

    Dan, you tell us how you used a web service (in CF I assume) to spawn the child threads? I need to run threads inside a thread. Thanks.

  • Dan | July 29, 2008

    @David: simply create a separate CFC with a method that spawns a new CFTHREAD. This method should have the access type "remote".

    Then, use the proper CFINVOKE call to the CFC webservice containing the method that spawns the CFTHREAD.

    On a side note, I keep track of all the threads I create using a structure and test for the status of the thread.

    HTH!