Windows System Software -- Consulting, Training, Development -- Unique Expertise, Guaranteed Results

Home NTDEV

Before Posting...

Please check out the Community Guidelines in the Announcements and Administration Category.

More Info on Driver Writing and Debugging


The free OSR Learning Library has more than 50 articles on a wide variety of topics about writing and debugging device drivers and Minifilters. From introductory level to advanced. All the articles have been recently reviewed and updated, and are written using the clear and definitive style you've come to expect from OSR over the years.


Check out The OSR Learning Library at: https://www.osr.com/osr-learning-library/


DLL's and stack variables

OSR_Community_UserOSR_Community_User Member Posts: 110,217
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

Comments

  • OSR_Community_UserOSR_Community_User Member Posts: 110,217
    ----- Original Message -----
    From: "Richard Hartman" <[email protected]>
    To: "NT Developers Interest List" <[email protected]>
    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: [email protected]
    > To unsubscribe send a blank email to $subst('Email.Unsub')
    >
  • OSR_Community_UserOSR_Community_User Member Posts: 110,217
    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
  • OSR_Community_UserOSR_Community_User Member Posts: 110,217
    > >> 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
  • OSR_Community_UserOSR_Community_User Member Posts: 110,217
    > 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
  • OSR_Community_UserOSR_Community_User Member Posts: 110,217
    > 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
  • OSR_Community_UserOSR_Community_User Member Posts: 110,217
    > 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 :-) 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 :-)

    Max
Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. Sign in or register to get started.

Upcoming OSR Seminars
OSR has suspended in-person seminars due to the Covid-19 outbreak. But, don't miss your training! Attend via the internet instead!
Kernel Debugging 13-17 May 2024 Live, Online
Developing Minifilters 1-5 Apr 2024 Live, Online
Internals & Software Drivers 11-15 Mar 2024 Live, Online
Writing WDF Drivers 20-24 May 2024 Live, Online