DLL's and stack variables

Globals within DLL’s are visible to all threads within a given process.
Locals (stack-based) are specific to each thread. What about locals with the
‘static’ keyword? Are they persistent, yet still specific to each thread? Or
are they treated as globals, with a single such variable visible to all threads?

RLH

----- Original Message -----
From: “Richard Hartman”
To: “NT Developers Interest List”
Sent: Wednesday, June 21, 2000 2:38 PM
Subject: [ntdev] DLL’s and stack variables

> Globals within DLL’s are visible to all threads within a given process.
> Locals (stack-based) are specific to each thread. What about locals with the
> ‘static’ keyword? Are they persistent, yet still specific to each thread? Or
> are they treated as globals, with a single such variable visible to all threads?
They are globals.

There is a declspec to declare globals as “thread local” also, but this DOES NOT
WORK IN DLLs. There’s a KB article on this somewhere, but the gist is that the
main program allocates the thread local storage prior to knowing anything about the
DLLs.
-DH

>
> RLH
>
>
> —
> You are currently subscribed to ntdev as: xxxxx@syssoftsol.com
> To unsubscribe send a blank email to $subst(‘Email.Unsub’)
>

At 02:44 PM 06/21/2000 -0400, Dave Harvey wrote:

> Globals within DLL’s are visible to all threads within a given process.
> Locals (stack-based) are specific to each thread. What about locals with the
> ‘static’ keyword? Are they persistent, yet still specific to each thread? Or
> are they treated as globals, with a single such variable visible to all
threads?
They are globals.

There is a declspec to declare globals as “thread local” also, but this
DOES NOT
WORK IN DLLs. There’s a KB article on this somewhere, but the gist is that the
main program allocates the thread local storage prior to knowing anything
about the
DLLs.

I noticed that warning, and thus I’m avoiding the declspec approach.

To be more complete, I’m working on a COM server written using ATL in VC6.
The intent is for this DLL to be used from IIS, which means it will be
accessed by IIS’s thread pool. Each time the DLL is to be used, the worker
thread must set a few properties and then invoke a method.

If multiple IIS worker threads are accessing the DLL in parallel, I’m
concerned that as each IIS worker thread sets various properties they will
be stepping all over each other’s values. So I need to make the property
storage specific to each worker thread. The properties must be stored in
persistent (non-stack-based) storage since there are multiple properties to
be set before the method call. But making them global means all threads in
the process will share a single, common copy of each property.

Reviewing MSDN’s comments on that topic, it appears that I may be able to
avoid this nightmare by proper selection of the COM threading model. The
docs suggest that the server can enforce the threading model of its choice,
and if the caller isn’t compliant COM will “magically” morph the call into
the proper threading model.

The terminology of COM and VC’s ATL wizard differ somewhat, so it’s unclear
which model (if any) will resolve this problem. I believe what I need to do
is cause each thread to run in its own “apartment”, with its own set of
globals within that apartment. Is that what VC’s ATL wizard calls the
“single” threading model? The “apartment” threading model?

As gross as this sounds: Would creating this COM server as an EXE resolve
the problem, by forcing a fresh copy of the EXE (a new process) to be
created for each IIS worker thread that needed such a server? I doubt it,
but someone here suggested that might be the case. I believe such an
out-of-process server would attempt to service all requests for that kind of
server, and we’d be right back to the same problem PLUS the overhead of
communicating across process boundaries.

Thanks!

RLH

> >> Globals within DLL’s are visible to all threads within a given

>> process. Locals (stack-based) are specific to each thread. What
>> about locals with the ‘static’ keyword? Are they persistent, yet
>> still specific to each thread? Or are they treated as globals,
>> with a single such variable visible to all threads?
>
>They are globals.
>
>There is a declspec to declare globals as “thread local” also, but
>this DOES NOT WORK IN DLLs. There’s a KB article on this
>somewhere, but the gist is that the main program allocates the
>thread local storage prior to knowing anything about the DLLs.

Richard,

Here is what you do:

In your DLL_PROCESS_ATTACH processing, do a TlsAlloc(), storing the
result in a global variable (let’s call it TlsIndex).

Have a data structure that contains all of your thread local storage
data. Let’s call it THREAD_DATA. You can put anything that you
would otherwise declare static in a function in this structure.

Write something like:

THREAD_DATA *
GetThreadData(
)
{
THREAD_DATA *ThreadData;

ThreadData = TlsGetValue(TlsIndex);
if (!ThreadData && !GetLastError())
{
/*
Allocate memory for ThreadData and initialize it as
needed.

*/
if (!TlsSetValue(TlsIndex, ThreadData))
{
/*
Do whatever you need to clean up ThreadData

*/
return 0;
}
}
return ThreadData;
}

Whenever you need thread local storage data, call GetThreadData() to
get it.

In DLL_PROCESS_DETACH, do get the thread data and free it. Then call
TlsFree(TlsIndex).

This method will guarantee that things don’t break even when the DLL
is dynamically loaded under any thread-creation situation (whether
threads are before, after, or during DLL loading).

Basically, GetThreadData() makes sure the current thread has its
TLS data initialized before you use it no matter what.

Hope this helps,

  • Danilo

> Globals within DLL’s are visible to all threads within a given process.

Locals (stack-based) are specific to each thread. What about locals with
the
‘static’ keyword? Are they persistent, yet still specific to each thread?
Or
are they treated as globals, with a single such variable visible to all
threads?

As globals, though invisible to other C files.
Use __declspec(thread) to maintain a thread-local static.
But note! The DLL which has __declspec(thread) constructs cannot be loaded
by LoadLibrary - only by LdrInitializeProcess during EXE’s or other DLL’s
import resolution.

Max

> There is a declspec to declare globals as “thread local” also, but this
DOES NOT

WORK IN DLLs. There’s a KB article on this somewhere, but the gist is
that

This only prevents the DLL to be loaded by LoadLibrary.
Otherwise - if the DLL is loaded by import resolution at process startup -
it is OK.

Max

> If multiple IIS worker threads are accessing the DLL in parallel, I’m

concerned that as each IIS worker thread sets various properties they will
be stepping all over each other’s values. So I need to make the property

The great idea is to call back to ASP engine by using IObjectContext (IIRC)
interface.
The C++ code called by COM from the ASP page has the access to ASP’s
Session/Application/Request/Response methods.
So - you can use ASP’s Application for your data and Application.Lock for
synchronization.

Reviewing MSDN’s comments on that topic, it appears that I may be able to
avoid this nightmare by proper selection of the COM threading model. The

No nightmare at all.

A COM thread can be:

  • STA0 - the first thread in the process which called CoInitialize
  • STA (Single Threaded Apartment) - any thread which called CoInitialize
  • MTA (Multi Threaded Apartment) - any thread which called CoInitializeEx
    with “multithreaded” flag (another flag for CoInitializeEx makes it a
    synonym for CoInitialize).
    Note that CoInitializeEx with “multithreaded” flag does real work only when
    called first time in the process - and is a noop after this. After this,
    any thread which did not called CoInitialize is treated as MTA thread.

Single-threaded (no registry value at all):

  • only a single thread (STA0) will enter this DLL. COM guarantees this.
    No synchronization at all.
    Apartment:
  • multiple threads (only STAs but not MTAs) can enter the DLL concurrently,
    but the object instance is owned by the thread which created it and is
    accessed by this thread only.
    Synchronization on global data, but not on object instances.
    Free:
  • multiple threads (only MTAs but not STAs) can enter the DLL concurrently,
    object instances are not owned by any thread - several threads can access
    the same instance simultaneously. Any COM callbacks (sinks) from the object
    instances to the caller code are guaranteed to be from the same thread which
    called the instance.
    Synchronization both on global data & on object instances.
    Both:
  • the same as Free, but no guarantee on callbacks (the component can even
    start its own thread and call sinks from it).
    Both STAs and MTAs can enter the DLL.
    Synchronization both on global data & on object instances.
    Rental (used by MTS components instead of Apartment in older MTS version):
  • multiple threads can enter the DLL concurrently, object instances are not
    owned by any thread - but the framework guarantees that only one thread at a
    time can access the given instance.
    Synchronization on global data, but not on object instances.

Creating an object instance from the wrong thread type will result in
internal proxy-stub pair and marshalling with performance losses.

Notes on EXE COM servers:

  • exporting a class by CoRegisterClassObject from the STA thread will lead
    to all calls from the abroad to this class delivered by this same thread. It
    must spin a message loop - COM calls to STAs from abroad are delivered as a
    WM_ to internal window. Surely, STA must not call WaitForSingleObject or
    something like this - the same way as the UI thread must not.
  • if you need a pool of STA threads in your EXE and distribute the instances
    evenly across them - use auto-thread class factory from ATL - read the
    source - this is interesting :slight_smile: This is practically the same what MTX.EXE
    does.
  • exporting a class by CoRegisterClassObject from the MTA thread will lead
    to all calls from the abroad to this class delivered by some random thread
    in RPC-created pool of server threads. These threads are free to call
    WaitForSingleObject or similar.

Proxies to the abroad objects are belonging to the apartment.
If STA thread received a proxy pointer - only this thread can access it.
If MTA thread received a proxy pointer - only MTA threads can access it, but
not STAs.
Violation of this rule will result in RPC_E_WRONG_THREAD.

For more information on COM threading, search MSDN for “MTA” and “STA”
terms. There are some new notions in it in w2k - IIRC connected with COM+.

docs suggest that the server can enforce the threading model of its
choice,
and if the caller isn’t compliant COM will “magically” morph the call into
the proper threading model.

…possibly with huge performance loss. The correct threading model for code
called from ASP is Apartment (at least it was so in IIS4).

As gross as this sounds: Would creating this COM server as an EXE resolve
the problem, by forcing a fresh copy of the EXE (a new process) to be
created for each IIS worker thread that needed such a server?

Huge performance loss. Using critical sections to guard globals in the
Apartment model DLL is much wiser solution. BTW - these critical sections
seems to be provided by ATL.

out-of-process server would attempt to service all requests for that kind
of
server, and we’d be right back to the same problem PLUS the overhead of
communicating across process boundaries.

How EXE COM server handles the incoming requests - see the above. You’re
right on overhead :slight_smile:

Max