Mixed-mode MC++ assembly - why don't I see THREAD_ATTACH with ASP.NET?

K

Kevin Frey

Using .NET 1.1.

We have a mixed-mode assembly written in Managed C++ that we are using from
an ASP.NET application that has been coded using C#. The mixed-mode assembly
has its own "initialisation" routine to cater for any potential "mixed-mode
DLL loading problem".

Some of our code uses thread-local-storage, and in the DllMain for my
mixed-mode assembly I did the appropriate initialisation of the
thread-local-storage object on THREAD_ATTACH, cleaning it up on
THREAD_DETACH.

When the assembly is used from ASP.NET, I don't see the THREAD_ATTACH or
DETACH.

Now I understand from reading Microsoft's documentation that
THREAD_ATTACH/DETACH are not posted to DllMain for pre-existing threads.

Looking at aspnet_wp.exe, on my machine (WXP Pro), it starts with 14 threads
and when I start a couple of web requests the number of threads increases to
17. Fairly soon my assembly fails because it is attempting to use the
thread-local-storage but the slot has not been initialised.

My workaround for the moment is to perform allocation of the
thread-local-storage object when it is requested, if it is not already
present, but I don't like this approach because if I don't see the
THREAD_ATTACH/DETACH I'll never know when to clean it up.

The most confusing thing is understanding how aspnet_wp.exe can create a new
thread but those threads don't produce corresponding THREAD_ATTACH'es in my
assembly.

Does anyone have any experience with this who might be able to offer an
explanation?

Thanks

Kevin
 
W

William DePalo [MVP VC++]

Kevin Frey said:
The most confusing thing is understanding how aspnet_wp.exe can create a
new thread but those threads don't produce corresponding THREAD_ATTACH'es
in my assembly.

You might want to post in an ASP.Net group.

But a possible explanation is that ASP.Net creates a thread pool in advance
of loading your assembly. Then it assigns a thread as necessary from the
pool to service the request which passes through your assembly. If that's
the case, you won't see the thread attach notification.

Regards,
Will
 
K

Kevin Frey

I'm also suspecting that the CLR is using DisableThreadLibraryCalls since I
know for a fact that the aspnet_wp.exe creates additional threads and I
don't see any notifications.

Kevin
 
W

Willy Denoyette [MVP]

No, the CLR doesn't use DisableThreadLibraryCalls.
What's happening is, that the worker process uses a pool of threads, when
the "very first" web request arrives, the poolmanager creates a minimum of
pooled threads (default is 3 for IIS5) and pulls a thread from the pool to
handle the request. Your request handler loads the DLL (delay loaded) as a
result of a call into your dll, but that's is after the minimum pool of
threads are created, so you don't see any DLL_THREAD_ATTACH. When the
request finishes, the thread returns to the pool where it wait's to handle
up-comming requests. That means that you won't see any DLL_THREAD_ATTACH as
long as there are free threads in the pool.

Willy.

| I'm also suspecting that the CLR is using DisableThreadLibraryCalls since
I
| know for a fact that the aspnet_wp.exe creates additional threads and I
| don't see any notifications.
|
| Kevin
|
| | > | >> The most confusing thing is understanding how aspnet_wp.exe can create
a
| >> new thread but those threads don't produce corresponding
THREAD_ATTACH'es
| >> in my assembly.
| >
| > You might want to post in an ASP.Net group.
| >
| > But a possible explanation is that ASP.Net creates a thread pool in
| > advance of loading your assembly. Then it assigns a thread as necessary
| > from the pool to service the request which passes through your assembly.
| > If that's the case, you won't see the thread attach notification.
| >
| > Regards,
| > Will
| >
| >
| >
|
|
 
K

Kevin Frey

OK. Thanks. So are you saying that IIS is creating the threads, not the
ASP.NET framework? If so, I'm confused, because aspnet_wp.exe is *not* IIS
and that's where I am seeing extra threads being created. Actually, thinking
about it a little, my confusion is even greater, because usually
"extensions" to IIS are in the form of an ISAPI DLL, yet here is an
*executable* getting run (somehow) by IIS. It's a side issue - but do you
know the process by which all this starts running?

Anyway, regardless of why I'm not getting the THREAD_ATTACH/DETACH
notifications, I still have a problem to solve, hence my next question...

If you wanted to use Thread-Local-Storage, then the absence of THREAD_ATTACH
notifications prevents you from initialising a "thread local storage object"
in advance. That's fine, because it can be solved just as easily with "lazy
instantiation" (eg. if p == null then instantiate the object).

Unfortunately the reverse is not true in terms of cleaning up these objects.
Since there is no THREAD_DETACH notification, there is no logical point (as
far as a .NET assembly is concerned) for cleaning up the thread's thread
local storage object. At the moment we are just letting it leak, until we
think of an idea.

How dangerous the leak is I guess depends on whether IIS or ASP.NET
terminates threads from the pool and then recreates them. In other words,
does the thread pool grow *and* shrink? Or does it only ever grow?

If the thread pool does shrink as well, then over time our memory leak could
become a problem (although the object in question is only about 30 bytes in
size, so it will require a large amount of threads to start/stop before
having any discernible memory impact).

So do you happen to have a plan of attack for handling this problem?

Thanks

Kevin
 
W

Willy Denoyette [MVP]

| OK. Thanks. So are you saying that IIS is creating the threads, not the
| ASP.NET framework? If so, I'm confused, because aspnet_wp.exe is *not* IIS
| and that's where I am seeing extra threads being created. Actually,
thinking
| about it a little, my confusion is even greater, because usually
| "extensions" to IIS are in the form of an ISAPI DLL, yet here is an
| *executable* getting run (somehow) by IIS. It's a side issue - but do you
| know the process by which all this starts running?
|

No, I'm talking about the "worker process" which is "aspnet_wp.exe" on W2K
and XP (different name different architecture on W2K3).
The "worker process" is handling all "asp.net" requests and it's started and
fed by the .NET ISAPI extension (aspnet_filter.dll), communication between
IIS (actually the ISAPI filter) and the worker process goes via a named
pipe.

| Anyway, regardless of why I'm not getting the THREAD_ATTACH/DETACH
| notifications, I still have a problem to solve, hence my next question...
|
| If you wanted to use Thread-Local-Storage, then the absence of
THREAD_ATTACH
| notifications prevents you from initialising a "thread local storage
object"
| in advance. That's fine, because it can be solved just as easily with
"lazy
| instantiation" (eg. if p == null then instantiate the object).
|
| Unfortunately the reverse is not true in terms of cleaning up these
objects.
| Since there is no THREAD_DETACH notification, there is no logical point
(as
| far as a .NET assembly is concerned) for cleaning up the thread's thread
| local storage object. At the moment we are just letting it leak, until we
| think of an idea.
|
| How dangerous the leak is I guess depends on whether IIS or ASP.NET
| terminates threads from the pool and then recreates them. In other words,
| does the thread pool grow *and* shrink? Or does it only ever grow?
|
The thread pool grows until it reaches the maximum threads (25 per CPU by
default but configurable), incomming requests are queued and picked up by a
free thread from the pool until the maximum number of threads is reached and
all threads are busy, that's the point where requests may get delayed. That
means that threads live as long as the application domain related to the web
application.
Whenever the worker process detects an application failure (out-of
memory/stack, whatever) or a deadlock at the level of the thread pool, it
will recycle the AD, that is, it unloads the AD which implicitely aborts all
threads (and releases the TLS) and unloads all assemblies loaded into this
AD, after that the worker recreate the AD for the application and waits for
new requests to arrive.
So, that means that the threads are kept alive as long as the AD exists, no
new threads are getting created, instead the existing threads are pulled
from the pool over and over again until the whole thing gets torned down.
Note that the AD is also unloaded after a time-out period during which no
new requests arrived.

| If the thread pool does shrink as well, then over time our memory leak
could
| become a problem (although the object in question is only about 30 bytes
in
| size, so it will require a large amount of threads to start/stop before
| having any discernible memory impact).
|

If, you read the above (and I hope I made it clear), you'll understand that
there is no issue (well I don't see any) with TLS in asp.net.

Willy.


| So do you happen to have a plan of attack for handling this problem?
|
| Thanks
|
| Kevin
|
| | > No, the CLR doesn't use DisableThreadLibraryCalls.
| > What's happening is, that the worker process uses a pool of threads,
when
| > the "very first" web request arrives, the poolmanager creates a minimum
of
| > pooled threads (default is 3 for IIS5) and pulls a thread from the pool
to
| > handle the request. Your request handler loads the DLL (delay loaded) as
a
| > result of a call into your dll, but that's is after the minimum pool of
| > threads are created, so you don't see any DLL_THREAD_ATTACH. When the
| > request finishes, the thread returns to the pool where it wait's to
handle
| > up-comming requests. That means that you won't see any DLL_THREAD_ATTACH
| > as
| > long as there are free threads in the pool.
| >
| > Willy.
| >
| > | > | I'm also suspecting that the CLR is using DisableThreadLibraryCalls
| > since
| > I
| > | know for a fact that the aspnet_wp.exe creates additional threads and
I
| > | don't see any notifications.
| > |
| > | Kevin
| > |
| > | | > | > | > | >> The most confusing thing is understanding how aspnet_wp.exe can
| > create
| > a
| > | >> new thread but those threads don't produce corresponding
| > THREAD_ATTACH'es
| > | >> in my assembly.
| > | >
| > | > You might want to post in an ASP.Net group.
| > | >
| > | > But a possible explanation is that ASP.Net creates a thread pool in
| > | > advance of loading your assembly. Then it assigns a thread as
| > necessary
| > | > from the pool to service the request which passes through your
| > assembly.
| > | > If that's the case, you won't see the thread attach notification.
| > | >
| > | > Regards,
| > | > Will
| > | >
| > | >
| > | >
| > |
| > |
| >
| >
|
|
 
K

Kevin Frey

Thanks, Willy. I wish I could have found such a fantastically clear
explanation of this on the web. You've been a great help.

Thanks

Kevin
 
W

Willy Denoyette [MVP]

Kevin, glad to help you out, thanks for the kind words.

Willy.


| Thanks, Willy. I wish I could have found such a fantastically clear
| explanation of this on the web. You've been a great help.
|
| Thanks
|
| Kevin
|
| | >
| >
| >
| > | > | OK. Thanks. So are you saying that IIS is creating the threads, not
the
| > | ASP.NET framework? If so, I'm confused, because aspnet_wp.exe is *not*
| > IIS
| > | and that's where I am seeing extra threads being created. Actually,
| > thinking
| > | about it a little, my confusion is even greater, because usually
| > | "extensions" to IIS are in the form of an ISAPI DLL, yet here is an
| > | *executable* getting run (somehow) by IIS. It's a side issue - but do
| > you
| > | know the process by which all this starts running?
| > |
| >
| > No, I'm talking about the "worker process" which is "aspnet_wp.exe" on
W2K
| > and XP (different name different architecture on W2K3).
| > The "worker process" is handling all "asp.net" requests and it's started
| > and
| > fed by the .NET ISAPI extension (aspnet_filter.dll), communication
between
| > IIS (actually the ISAPI filter) and the worker process goes via a named
| > pipe.
| >
| > | Anyway, regardless of why I'm not getting the THREAD_ATTACH/DETACH
| > | notifications, I still have a problem to solve, hence my next
| > question...
| > |
| > | If you wanted to use Thread-Local-Storage, then the absence of
| > THREAD_ATTACH
| > | notifications prevents you from initialising a "thread local storage
| > object"
| > | in advance. That's fine, because it can be solved just as easily with
| > "lazy
| > | instantiation" (eg. if p == null then instantiate the object).
| > |
| > | Unfortunately the reverse is not true in terms of cleaning up these
| > objects.
| > | Since there is no THREAD_DETACH notification, there is no logical
point
| > (as
| > | far as a .NET assembly is concerned) for cleaning up the thread's
thread
| > | local storage object. At the moment we are just letting it leak, until
| > we
| > | think of an idea.
| > |
| > | How dangerous the leak is I guess depends on whether IIS or ASP.NET
| > | terminates threads from the pool and then recreates them. In other
| > words,
| > | does the thread pool grow *and* shrink? Or does it only ever grow?
| > |
| > The thread pool grows until it reaches the maximum threads (25 per CPU
by
| > default but configurable), incomming requests are queued and picked up
by
| > a
| > free thread from the pool until the maximum number of threads is reached
| > and
| > all threads are busy, that's the point where requests may get delayed.
| > That
| > means that threads live as long as the application domain related to the
| > web
| > application.
| > Whenever the worker process detects an application failure (out-of
| > memory/stack, whatever) or a deadlock at the level of the thread pool,
it
| > will recycle the AD, that is, it unloads the AD which implicitely aborts
| > all
| > threads (and releases the TLS) and unloads all assemblies loaded into
this
| > AD, after that the worker recreate the AD for the application and waits
| > for
| > new requests to arrive.
| > So, that means that the threads are kept alive as long as the AD exists,
| > no
| > new threads are getting created, instead the existing threads are pulled
| > from the pool over and over again until the whole thing gets torned
down.
| > Note that the AD is also unloaded after a time-out period during which
no
| > new requests arrived.
| >
| > | If the thread pool does shrink as well, then over time our memory leak
| > could
| > | become a problem (although the object in question is only about 30
bytes
| > in
| > | size, so it will require a large amount of threads to start/stop
before
| > | having any discernible memory impact).
| > |
| >
| > If, you read the above (and I hope I made it clear), you'll understand
| > that
| > there is no issue (well I don't see any) with TLS in asp.net.
| >
| > Willy.
| >
| >
| > | So do you happen to have a plan of attack for handling this problem?
| > |
| > | Thanks
| > |
| > | Kevin
| > |
| > | | > | > No, the CLR doesn't use DisableThreadLibraryCalls.
| > | > What's happening is, that the worker process uses a pool of threads,
| > when
| > | > the "very first" web request arrives, the poolmanager creates a
| > minimum
| > of
| > | > pooled threads (default is 3 for IIS5) and pulls a thread from the
| > pool
| > to
| > | > handle the request. Your request handler loads the DLL (delay
loaded)
| > as
| > a
| > | > result of a call into your dll, but that's is after the minimum pool
| > of
| > | > threads are created, so you don't see any DLL_THREAD_ATTACH. When
the
| > | > request finishes, the thread returns to the pool where it wait's to
| > handle
| > | > up-comming requests. That means that you won't see any
| > DLL_THREAD_ATTACH
| > | > as
| > | > long as there are free threads in the pool.
| > | >
| > | > Willy.
| > | >
| > | > | > | > | I'm also suspecting that the CLR is using
DisableThreadLibraryCalls
| > | > since
| > | > I
| > | > | know for a fact that the aspnet_wp.exe creates additional threads
| > and
| > I
| > | > | don't see any notifications.
| > | > |
| > | > | Kevin
| > | > |
| > message
| > | > | | > | > | > | > | > | >> The most confusing thing is understanding how aspnet_wp.exe can
| > | > create
| > | > a
| > | > | >> new thread but those threads don't produce corresponding
| > | > THREAD_ATTACH'es
| > | > | >> in my assembly.
| > | > | >
| > | > | > You might want to post in an ASP.Net group.
| > | > | >
| > | > | > But a possible explanation is that ASP.Net creates a thread pool
| > in
| > | > | > advance of loading your assembly. Then it assigns a thread as
| > | > necessary
| > | > | > from the pool to service the request which passes through your
| > | > assembly.
| > | > | > If that's the case, you won't see the thread attach
notification.
| > | > | >
| > | > | > Regards,
| > | > | > Will
| > | > | >
| > | > | >
| > | > | >
| > | > |
| > | > |
| > | >
| > | >
| > |
| > |
| >
| >
|
|
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top