AppDomains and static unmanaged variables

S

Stephen Walch

Our application environment consists of three basic layers:

1. Third-party unmanaged DLLs that were written before the CLR was invented
and maintain a significant amount of information (including memory
management and connection pooling constructs) as static variables which were
intended to scoped to the process.

2. Managed C++ assemblies (.NET 1.1) that wrap the unmanaged DLLs as nice
neat classes with managed interfaces.

3. ASP.NET 1.1 applications that call the managed assemblies.

When running just one ASP.NET web app, everything is fine, even with
high-volume multi-threaded access. But introduce a second web app in the
same AppPool, and eventually the state of the unmanaged DLLs in #1 become
corrupted. The only explanation I can come up with is that calling the
unmanaged DLLs from multiple AppDomains is breaking the assumption that the
original developers made that static variable are global to the process.

One workaround that seems to work is putting the web apps in different
AppPools, and thus different processes. Unfortunately, however, this is not
an option in some cases (for example, when writting an addin for
SharePoint).

I cannot recode the unmanaged DLL, as it was written by a third party. So
my only choice is to code some protection into my wrapper classes. For
example, is there a way to force all accesses to my wrapper class (or
wrapper assembly) to be marshaled over to a singleton AppDomain? Or perhaps
a way to force static variables to be global in a process again?

In the old COM days, when threading was our biggest problem, we has
apartment models to protect legacy code that code not handle
multi-threading. Is there nothing to protect legacy code that does not
understand about AppDomains?

Thanks!
 
C

Carl Daniel [VC++ MVP]

Stephen said:
Our application environment consists of three basic layers:

1. Third-party unmanaged DLLs that were written before the CLR was
invented and maintain a significant amount of information (including
memory management and connection pooling constructs) as static
variables which were intended to scoped to the process.
Check.


2. Managed C++ assemblies (.NET 1.1) that wrap the unmanaged DLLs as
nice neat classes with managed interfaces.
OK.


3. ASP.NET 1.1 applications that call the managed assemblies.
Cool.


When running just one ASP.NET web app, everything is fine, even with
high-volume multi-threaded access. But introduce a second web app in
the same AppPool, and eventually the state of the unmanaged DLLs in
#1 become corrupted. The only explanation I can come up with is that
calling the unmanaged DLLs from multiple AppDomains is breaking the
assumption that the original developers made that static variable are
global to the process.

Unmanaged statics ARE global to the process. Managed statics are unique to
each AppDomain.
One workaround that seems to work is putting the web apps in different
AppPools, and thus different processes. Unfortunately, however, this
is not an option in some cases (for example, when writting an addin
for SharePoint).

I cannot recode the unmanaged DLL, as it was written by a third
party. So my only choice is to code some protection into my wrapper
classes. For example, is there a way to force all accesses to my
wrapper class (or wrapper assembly) to be marshaled over to a
singleton AppDomain? Or perhaps a way to force static variables to
be global in a process again?

To force all access to your code into a single AppDomain, you'd have to put
code in your managed layer to do the marshalling. This sounds complicated.

You unmanaged static varaible are already global to the process, so I
suspect that you've misdiagnosed the problem. Could it be that you have
managed static variables that need to be process-global as well?
In the old COM days, when threading was our biggest problem, we has
apartment models to protect legacy code that code not handle
multi-threading. Is there nothing to protect legacy code that does
not understand about AppDomains?

Unmanaged code cannot readily detect that AppDomains even exist. Unmanaged
statics still have process-global scope, and unmanaged code sees all threads
as being equal, with know knowledge that they might be coming from different
app domains.

-cd
 
S

Stephen Walch

You may be right that I have misdiagnosed the problem. My Managed C++
wrapper has statics that managed access to unmanaged statics. So if the
managed statics are scoped to teh AppDomain and the unmanaged statics are
scoped to the process, I have a big problem.

To use a concrete example, one of my managed classes has a static Monitor
instance that was supposed to make sure that no two threads attempted to
initialize the same unmanaged static state at the same time. Now I see that
is a two AppDomain scenario I actually have two Monitors and am not really
protecting the unmanaged state the way I thought I was.

So what is the answer to this? Is there any way to have a PROCESS GLOBAL
monitor, reference counter, memory handle, etc. in an unmanaged app?
 
S

Stephen Walch

[sorry, that last post had a lot of typos. let me try again...]

You may be right that I have misdiagnosed the problem. My Managed C++
wrapper has statics that manage access to statics in the unmanaged DLL. So
if the managed statics are scoped to the AppDomain and the unmanaged statics
are scoped to the process, I have a big problem.

To use a concrete example, one of my managed classes has a static Monitor
instance that was supposed to make sure that no two threads attempt to
initialize the same unmanaged static state at the same time. Now I see that
in a two AppDomain scenario, I actually have two Monitors and am not really
protecting the unmanaged state the way I thought I was.

So what is the answer to this? Is there any way to have a PROCESS GLOBAL
monitor, reference counter, memory handle, etc. in an managed app?
 
C

Carl Daniel [VC++ MVP]

Stephen Walch said:
[sorry, that last post had a lot of typos. let me try again...]

You may be right that I have misdiagnosed the problem. My Managed C++
wrapper has statics that manage access to statics in the unmanaged DLL.
So
if the managed statics are scoped to the AppDomain and the unmanaged
statics are scoped to the process, I have a big problem.

To use a concrete example, one of my managed classes has a static Monitor
instance that was supposed to make sure that no two threads attempt to
initialize the same unmanaged static state at the same time. Now I see
that
in a two AppDomain scenario, I actually have two Monitors and am not
really
protecting the unmanaged state the way I thought I was.

So what is the answer to this? Is there any way to have a PROCESS GLOBAL
monitor, reference counter, memory handle, etc. in an managed app?

I have a vagure recollection of an attribute that can be applied to statics
to make them process-global, but I can't find anything like that in .NET
docs, so I'm probably misremembering something.

A couple of options come to mind:
1. Move all of your statics into unmanaged code.
2. Use marshalling to send all calls to this code into a single AppDomain.
You'd have to store a reference to the singleton AppDomain somewhere that
every other AppDomain could get at it. For example, in an unmanaged static.

-cd
 
S

Stephen Walch

Hi Carl, Your helpful comments have pointed me in the right direction. I
have changed much of my "global" state management to use unmanaged static
variables and it has produced very encouraging results (i.e. no corruption).

I have not yet figured out how to move certain managed classes, such as
HashTable, to global state. I was able to declare a singleton instance

static gcroot<HashTable*> g_table;

but it was only accessible from the AppDomain in which I initialized it. If
I try to access it from another AppDomain, I get the old "Cant pass a
GCHandle across AppDomains" message. Is there a better way? Or do I simply
need to find unmanaged equivalents to these classes?

Carl Daniel said:
Stephen Walch said:
[sorry, that last post had a lot of typos. let me try again...]

You may be right that I have misdiagnosed the problem. My Managed C++
wrapper has statics that manage access to statics in the unmanaged DLL.
So
if the managed statics are scoped to the AppDomain and the unmanaged
statics are scoped to the process, I have a big problem.

To use a concrete example, one of my managed classes has a static Monitor
instance that was supposed to make sure that no two threads attempt to
initialize the same unmanaged static state at the same time. Now I see
that
in a two AppDomain scenario, I actually have two Monitors and am not
really
protecting the unmanaged state the way I thought I was.

So what is the answer to this? Is there any way to have a PROCESS GLOBAL
monitor, reference counter, memory handle, etc. in an managed app?

I have a vagure recollection of an attribute that can be applied to
statics to make them process-global, but I can't find anything like that
in .NET docs, so I'm probably misremembering something.

A couple of options come to mind:
1. Move all of your statics into unmanaged code.
2. Use marshalling to send all calls to this code into a single AppDomain.
You'd have to store a reference to the singleton AppDomain somewhere that
every other AppDomain could get at it. For example, in an unmanaged
static.

-cd
 
C

Carl Daniel [VC++ MVP]

Stephen said:
Hi Carl, Your helpful comments have pointed me in the right
direction. I have changed much of my "global" state management to
use unmanaged static variables and it has produced very encouraging
results (i.e. no corruption).
I have not yet figured out how to move certain managed classes, such
as HashTable, to global state. I was able to declare a singleton
instance
static gcroot<HashTable*> g_table;

but it was only accessible from the AppDomain in which I initialized
it. If I try to access it from another AppDomain, I get the old
"Cant pass a GCHandle across AppDomains" message. Is there a better
way? Or do I simply need to find unmanaged equivalents to these
classes?

I guess you just need an unmanaged equivalent. I know that within the
implementation of the CLR there is the concept of app-domain neutral code
and data, but I don't think there's any way to make use of it from user
code.

-cd
 

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