Jeff and .NET

The .NET musings of Jeff Putz

Sponsors

News

My Sites

Archives

How do I find the application path in an HttpModule timer?

Here's a pickle (to me, anyway)... How do you determine an application's path from an HttpModule inside of a running timer? I tried the obvious by passing in the application context and looking it up from Request.ApplicationPath, but that works only when the app first starts (which makes sense since there is no request when the timer fires). Code looks something like this...

    public class Module : IHttpModule
    {
        public void Init(HttpApplication application)
        {
            timer = new Timer(new TimerCallback(this.myMethod),
                application.Context, TimeSpan.Zero, new TimeSpan(0, 15, 0));
        }

        static Timer timer;

        private void myMethod(object sender)
        {
            // this works the first time, but not on subsequent calls
            string path = ((HttpContext)sender).Request.ApplicationPath;
        }

        public void Dispose()
        {
            timer.Dispose();
        }
}

Posted: Feb 17 2005, 04:22 PM by Jeff | with 23 comment(s)
Filed under:

Comments

Brock Allen said:

You can't do what you're trying to do. Since you're using a timer, it might go off when there are no users accessing your application. Thus there's no HttpContext, thus no Request, etc.

Based upon what you're doing, I'd suggest caching the application path in a static field. You'll need to do this when your Init fires.

-Brock
# February 17, 2005 4:51 PM

Brock Allen said:

Oh, also note that there might be many instances of your Module created by ASP.NET, thus

1) You could end up with many timers scheduled. Is this what you intended?

2) You're using a static filed to store the timer. Each Module created will overwrite the previous timer reference. Your Dispose() only cleans up one of them, since it's in the static field. You'll end up leaking Timer objects. I doubt you want this.


# February 17, 2005 4:57 PM

Jeff said:

Why would there be more than one instance of the module? I have never, ever noticed any behavior that would indicate there was more than one of any module.
# February 17, 2005 5:06 PM

Brock Allen said:

Because that's how it's implemented. Have you tried to test more than one client at a time against your application?

Check out HttpApplicationFactory.GetNormalApplicationInstance. This is what the HttpRuntime calls to get an Application and it's set of modules. These tuples are all pooled. This means there are potentially many instances of your HttpApplication-derived class created also (the one from global.asax).

# February 17, 2005 5:17 PM

James said:

Jeff, a better method is the static HttpRuntime.AppDomainAppPath property.
# February 17, 2005 5:20 PM

Brock Allen said:

Ok. I was just trying to help. Good luck.
# February 17, 2005 5:22 PM

Jeff said:

Well, first you said it couldn't be done, and James gave a working solution. Then you made statements about a class that isn't even documented in the SDK, so naturally I'm skeptical without a more thorough explanation.

Don't get me wrong, I appreciate the help, but making unqualified statements makes things difficult.
# February 17, 2005 5:32 PM

Jeff said:

And where does it say there are multiple module instances?

What I'm doing in that code is the same thing I've done a hundred times to track users around a site. Rob Howard does the very same thing in his forum (on asp.net or communityserver.org). He'd know better than any of us that it's an acceptable thing to do.
# February 17, 2005 5:50 PM

James said:

Nice link Brock. I didn't know a pool of HttpApplication objects were maintained and reused.

Jeff since you don't need a request, you could either start the timer on Application_Start, or keep doing what you're doing and test in the module if timer is null before creating one (doing double check locking obviously).

Oh, fyi the Cache is also stored on the HttpRuntime :)
# February 17, 2005 5:51 PM

Drew Marsh said:

Ummm... your module instance is guarenteed to be used only by that HttpApplication instance (that's the contract). So all you need to do is stuff the application instance handed to you in Init into a member variable and then in your timer call back you use the same varible.

HTH,
Drew
# February 18, 2005 4:32 PM

Drew Marsh said:

Also the fact that you use a static Timer variable is also technically a "strange" design. You should make it an instance variable. Like I said the HttpApplication instantiates an instance of your module and their lifetimes are (basically) tied to one another. Now in a web farm there N applications running so using a static is kinda confusing. In fact, the only reason you get away with it is because each application is separated by an AppDomain which is the boundary for static variables[1].

HTH,
Drew

[1] http://blogs.msdn.com/cbrumme/archive/2003/04/15/51317.aspx
# February 18, 2005 4:36 PM

Drew Marsh said:

Oh and since I didn't actually address your problem (sorry):

First Brock's advice about the HttpContext not being available is absolultely right. The only properties you can safely access from your own thread is Application, Modules and Server. However, since the application path is determined once for the lifetime of the application instance you can just grab *that* in Init and save that as a member variable since, as you observed, Context is available in Init.

HTH,
Drew
# February 18, 2005 5:02 PM

Jeff said:

OK... I got some clarification on some of these things from Rob Howard. Yes, it's true that each request gets an instance of the module, however, it's that very reason that you MUST use a static variable for the timer, or you'll start firing off those things all over the place and kill your thread pool in virtually no time.
# February 19, 2005 1:50 AM

Brock Allen said:

"OK... I got some clarification on some of these things from Rob Howard. Yes, it's true that each request gets an instance of the module, however, it's that very reason that you MUST use a static variable for the timer, or you'll start firing off those things all over the place and kill your thread pool in virtually no time."

Indeed. That's why creating a new one per instance of the Module in Init is not what I thought you had intended (thus my comments). The code as it stands above is insufficient. Here's a quick rewrite from memory of how it'd have to be given all the nasty goo that one can encounter:

public class Module : IHttpModule
{
public void Init(HttpApplication application)
{
if (timer == null)
{
lock (TheLock)
{
if (timer == null)
{
AppPath = HttpContext.Current.Request.ApplicationPath;

Timer temp = new Timer(new TimerCallback(this.myMethod),
application.Context, TimeSpan.Zero, new TimeSpan(0, 15, 0));
System.Threading.Thread.MemoryBarrier();
timer = temp;
}
}
}
}

static string AppPath;
static Timer timer;
static object TheLock = new object();

private void myMethod(object sender)
{
string path = AppPath;
// other stuff ...
}

public void Dispose()
{
if (timer != null)
{
lock (TheLock)
{
if (timer != null)
{
timer.Dispose();
timer = null;
}
}
}
}
}

It's possible that all of that Init stuff could have been done in a static constructor, but I'm not sure if the CLR guarentees that all static ctors have *completed* execution prior to a second thread accessing accessing the type. Also, as for the Dispose() and the double lock, I'm not too sure if it's needed.... I'd need to spend some time thinking about that one. But, as it's there now, it's perhaps over cautious, but like I said, it may not be needed.

-Brock
# February 20, 2005 11:47 PM

Brock Allen said:

Ick, sorry for the really bad code formatting. :(
# February 20, 2005 11:47 PM

Jeff said:

While I get what you're saying, the truth is that my Timer NEVER fires more frequently than scheduled. Considering it's set to fire right away, you'd think then by your logic that it would be firing constantly, but this is not the case.
# February 21, 2005 12:15 AM

Brock Allen said:

"While I get what you're saying, the truth is that my Timer NEVER fires more frequently than scheduled. Considering it's set to fire right away, you'd think then by your logic that it would be firing constantly, but this is not the case. "

It's not the Timer that's important to see more than once, it's the ctor for the Module. As I asked before, have you done any load testing? More than just your one client browser? Since this is a timing issue I'd run many clients and have my test pages take many seconds to render (Thread.Sleep(10), perhaps) to see the Modules under pseudo-stress.

Anyway, I can see you're quite obstinate about this issue. Honestly my point wasn't to criticize you, but you did ask for comments and put your code out for scrutiny. I was simply trying to tell you what I knew of the conditions that your code would see. Just because you don't see this condition today doesn't mean it doesn't or won't exist.

I'll leave it at that. Once again, good luck.
# February 21, 2005 12:38 AM

Jeff said:

I'm not taking it as criticism... what I describe has been in use with thousands of simultaneous users in a real live application. It has long since moved beyond testing.
# February 21, 2005 1:36 AM

Drew Marsh said:

I think you're still misunderstanding the architecture...

"OK... I got some clarification on some of these things from Rob Howard. Yes, it's true that each request gets an instance of the module,"

Actually this is false. Either you misunderstood Rob or Rob misunderstood your question. Each request *does not* get it's own instance of the module. Each instance of an HttpApplication that uses the module gets it's own instance of the module. The module instance is then held onto for the life of the application and is only disposed of when the HttpApplication is shut down.

"however, it's that very reason that you MUST use a static variable for the timer, or you'll start firing off those things all over the place and kill your thread pool in virtually no time."

Your module's Init method is called one time when the module is loaded for the application. Init is not called for each request for example, which is what I think you're thinking happens.

HTH,
Drew
# February 22, 2005 4:42 PM

Jeff said:

No, that's not what I thought... I just repeated what he said incorrectly. I understand that it's one instance per app instance, not request.

And I did some testing on this as well. I had a module log when it was created in Init and when it died in Dispose. Total number of instances in the test (with 200k requests)? Two.

I'll make sure to test to see if the static variable has an instance of the timer each time and not create another one if it's there. Problem solved.
# February 22, 2005 5:36 PM

Drew Marsh said:

Jeff,

Ah, ok cool. Glad to hear you worked it all out. :)

I'm still not sure where you'd end up with more than one instance unless your HttpApplication was recycled, in which case the AppDomain should have been recycled too thus cleaning up your static. I would place my money on not even needing to check if the timer is already there since it should never be there unless there's a serious flaw in the ASP.NET runtime.

Happy coding,
Drew
# February 22, 2005 6:03 PM

fx said:

AppDomain.CurrentDomain.SetupInformation
# March 3, 2005 5:44 PM
Leave a Comment

(required) 

(required) 

(optional)

(required)