z

BLOG ARTICLE Win32 | 5 ARTICLE FOUND

  1. 2007.10.19 Win32 TLS (Thread Local Storage)
  2. 2007.05.04 Quick overview of how processes exit on Windows XP
  3. 2006.11.24 Using Event Object
  4. 2006.11.24 Dll Class Exports
  5. 2006.11.24 Pointer Safe Delete Macro


strtok같은 라이브러리 함수내에서 스태틱 변수 이용 시 멀티 스레드가 접근할 경우 내부 정적 변수가 변경 될 수 있으므로 이를 방지한다,  스레드 마다 다른 정적 변수 참조할 수 있도록 윈도우에서 제공한다.  이는 사용자 프로그램 내에 정적 변수와는 다르게 라이브러리 (윈도우) 함수 내의 정적 변수 초기화에 영향을 준다. VC 컴파일 옵션 중 Single Thread/Mulit Thread Mode가 그것이다.

AND

(원문 http://blogs.msdn.com/oldnewthing/archive/2007/05/03/2383346.aspx)

Exiting is one of the scariest moments in the lifetime of a process. (Sort of how landing is one of the scariest moments of air travel.)

Many of the details of how processes exit are left unspecified in Win32, so different Win32 implementations can follow different mechanisms.
For example, Win32s, Windows 95, and Windows NT all shut down processes differently. (I wouldn't be surprised if Windows CE uses yet another different mechanism.)
Therefore, bear in mind that what I write in this mini-series is implementation detail and can change at any time without warning. I'm writing about it because these details can highlight bugs lurking in your code. In particular, I'm going to discuss the way processes exit on Windows XP.

I should say up front that I do not agree with many steps in the way processes exit on Windows XP. The purpose of this mini-series is not to justify the way processes exit but merely to fill you in on some of the behind-the-scenes activities so you are better-armed when you have to investigate into a mysterious crash or hang during exit. (Note that I just refer to it as the way processes exit on Windows XP rather than saying that it is how process exit is designed. As one of my colleagues put it, "Using the word design to describe this is like using the term swimming pool to refer to a puddle in your garden.")

When your program calls ExitProcess a whole lot of machinery springs into action. First, all the threads in the process (except the one calling ExitProcess) are forcibly terminated. This dates back to the old-fashioned theory on how processes should exit: Under the old-fashioned theory, when your process decides that it's time to exit, it should already have cleaned up all its threads. The termination of threads, therefore, is just a safety net to catch the stuff you may have missed. It doesn't even wait two seconds first.

Now, we're not talking happy termination like ExitThread; that's not possible since the thread could be in the middle of doing something. Injecting a call to ExitThread would result in DLL_THREAD_DETACH notifications being sent at times the thread was not prepared for. Nope, these threads are terminated in the style of TerminateThread: Just yank the rug out from under it. Buh-bye. This is an ex-thread.

Tell, that was a pretty drastic move, now, wasn't it. And all this after the scary warnings in MSDN that TerminateThread is a bad function that should be avoided!

Wait, it gets worse.

Some of those threads that got forcibly terminated may have owned critical sections, mutexes, home-grown synchronization primitives (such as spin-locks), all those things that the one remaining thread might need access to during its DLL_PROCESS_DETACH handling. Well, mutexes are sort of covered; if you try to enter that mutex, you'll get the mysterious WAIT_ABANDONED return code which tells you that "Uh-oh, things are kind of messed up."

What about critical sections? There is no "Uh-oh" return value for critical sections; EnterCriticalSection doesn't have a return value. Instead, the kernel just says "Open season on critical sections!" I get the mental image of all the gates in a parking garage just opening up and letting anybody in and out.

As for the home-grown stuff, well, you're on your own.

This means that if your code happened to have owned a critical section at the time somebody called ExitProcess, the data structure the critical section is protecting has a good chance of being in an inconsistent state. (Afer all, if it were consistent, you probably would have exited the critical section! Well, assuming you entered the critical section because you were updating the structure as opposed to reading it.) Your DLL_PROCESS_DETACH code runs, enters the critical section, and it succeeds because "all the gates are up". Now your DLL_PROCESS_DETACH code starts behaving erratically because the values in that data structure are inconsistent.

Oh dear, now you have a pretty ugly mess on your hands.

And if your thread was terminated while it owned a spin-lock or some other home-grown synchronization object, your DLL_PROCESS_DETACH will most likely simply hang indefinitely waiting patiently for that terminated thread to release the spin-lock (which it never will do).

But wait, it gets worse. That critical section might have been the one that protects the process heap! If one of the threads that got terminated happened to be in the middle of a heap function like HeapAllocate or LocalFree, then the process heap may very well be inconsistent. If your DLL_PROCESS_DETACH tries to allocate or free memory, it may crash due to a corrupted heap.

Moral of the story: If you're getting a DLL_PROCESS_DETACH due to process termination,† don't try anything clever. Just return without doing anything and let the normal process clean-up happen. The kernel will close all your open handles to kernel objects. Any memory you allocated will be freed automatically when the process's address space is torn down. Just let the process die a quiet death.

Note that if you were a good boy and cleaned up all the threads in the process before calling ExitThread, then you've escaped all this craziness, since there is nothing to clean up.

Note also that if you're getting a DLL_PROCESS_DETACH due to dynamic unloading, then you do need to clean up your kernel objects and allocated memory because the process is going to continue running. But on the other hand, in the case of dynamic unloading, no other threads should be executing code in your DLL anyway (since you're about to be unloaded), so—assuming you coded up your DLL correctly—none of your critical sections should be held and your data structures should be consistent.

Hang on, this disaster isn't over yet. Even though the kernel went around terminating all but one thread in the process, that doesn't mean that the creation of new threads is blocked. If somebody calls CreateThread in their DLL_PROCESS_DETACH (as crazy as it sounds), the thread will indeed be created and start running! But remember, "all the gates are up", so your critical sections are just window dressing to make you feel good.

(The ability to create threads after process termination has begun is not a mistake; it's intentional and necessary. Thread injection is how the debugger breaks into a process. If thread injection were not permitted, you wouldn't be able to debug process termination!)

Next time, we'll see how the way process termination takes place on Windows XP caused not one but two problems.

Footnotes

†Everybody reading this article should already know how to determine whether this is the case. I'm assuming you're smart. Don't disappoint me.

AND

Using Event Object

개발/Windows 2006. 11. 24. 15:07


Example Code
For an example that uses CreateEvent, see Using Event Objects.

Requirements
Client: Included in Windows XP, Windows 2000 Professional, Windows NT Workstation, Windows Me, Windows 98, and Windows 95.
Server: Included in Windows Server 2003, Windows 2000 Server, and Windows NT Server.
Unicode: Implemented as Unicode and ANSI versions. Note that Unicode support on Windows Me/98/95 requires Microsoft Layer for Unicode.
Header: Declared in Winbase.h; include Windows.h.
Library: Use Kernel32.lib.


See Also
Synchronization Overview, Synchronization Functions, CloseHandle, CreateProcess, DuplicateHandle, OpenEvent, ResetEvent, SECURITY_ATTRIBUTES, SetEvent, Object Names

Using Event Objects

Applications use event objects in a number of situations to notify a waiting thread of the occurrence of an event. For example, overlapped I/O operations on files, named pipes, and communications devices use an event object to signal their completion. For more information about the use of event objects in overlapped I/O operations, see Synchronization and Overlapped Input and Output.


In the following example, an application uses event objects to prevent several threads from reading from a shared memory buffer while a master thread is writing to that buffer. First, the master thread uses the CreateEvent function to create a manual-reset event object. The master thread sets the event object to nonsignaled when it is writing to the buffer and then resets the object to signaled when it has finished writing. Then it creates several reader threads and an auto-reset event object for each thread. Each reader thread sets its event object to signaled when it is not reading from the buffer.

#define NUMTHREADS 4

HANDLE hGlobalWriteEvent;

void CreateEventsAndThreads(void)
{
  HANDLE hReadEvents[NUMTHREADS], hThread;
  DWORD i, IDThread;

  // Create a manual-reset event object. The master thread sets
  // this to nonsignaled when it writes to the shared buffer.

  hGlobalWriteEvent = CreateEvent(
       NULL,         // no security attributes
       TRUE,         // manual-reset event
       TRUE,         // initial state is signaled
       "WriteEvent"  // object name
       );

  if (hGlobalWriteEvent == NULL)
  {
       // error exit
  }

  // Create multiple threads and an auto-reset event object
  // for each thread. Each thread sets its event object to
  // signaled when it is not reading from the shared buffer.

  for(i = 1; i <= NUMTHREADS; i++)
  {
       // Create the auto-reset event.
       hReadEvents[i] = CreateEvent(
           NULL,     // no security attributes
           FALSE,    // auto-reset event
           TRUE,     // initial state is signaled
           NULL);    // object not named

       if (hReadEvents[i] == NULL)
       {
           // Error exit.
       }

       hThread = CreateThread(NULL, 0,
           (LPTHREAD_START_ROUTINE) ThreadFunction,
           &hReadEvents[i],  // pass event handle
           0, &IDThread);
       if (hThread == NULL)
       {
           // Error exit.
       }
  }
}
Before the master thread writes to the shared buffer, it uses the ResetEvent function to set the state of hGlobalWriteEvent (an application-defined global variable) to nonsignaled. This blocks the reader threads from starting a read operation. The master then uses the WaitForMultipleObjects function to wait for all reader threads to finish any current read operations. When WaitForMultipleObjects returns, the master thread can safely write to the buffer. After it has finished, it sets hGlobalWriteEvent and all the reader-thread events to signaled, enabling the reader threads to resume their read operations.

VOID WriteToBuffer(VOID)
{
  DWORD dwWaitResult, i;

  // Reset hGlobalWriteEvent to nonsignaled, to block readers.

  if (! ResetEvent(hGlobalWriteEvent) )
  {
       // Error exit.
  }

  // Wait for all reading threads to finish reading.

  dwWaitResult = WaitForMultipleObjects(
       NUMTHREADS,   // number of handles in array
       hReadEvents,  // array of read-event handles
       TRUE,         // wait until all are signaled
       INFINITE);    // indefinite wait

  switch (dwWaitResult)
  {
       // All read-event objects were signaled.
       case WAIT_OBJECT_0:
           // Write to the shared buffer.
           break;

       // An error occurred.
       default:
           printf("Wait error: %d\n", GetLastError());
           ExitProcess(0);
  }

  // Set hGlobalWriteEvent to signaled.

  if (! SetEvent(hGlobalWriteEvent) )
  {
       // Error exit.
  }

  // Set all read events to signaled.
  for(i = 1; i <= NUMTHREADS; i++)
       if (! SetEvent(hReadEvents[i]) )
       {
           // Error exit.
       }
}

Before starting a read operation, each reader thread uses WaitForMultipleObjects to wait for the application-defined global variable hGlobalWriteEvent and its own read event to be signaled. When WaitForMultipleObjects returns, the reader thread's auto-reset event has been reset to nonsignaled. This blocks the master thread from writing to the buffer until the reader thread uses the SetEvent function to set the event's state back to signaled.

VOID ThreadFunction(LPVOID lpParam)
{
  DWORD dwWaitResult;
  HANDLE hEvents[2];

  hEvents[0] = *(HANDLE*)lpParam;  // thread's read event
  hEvents[1] = hGlobalWriteEvent;

  dwWaitResult = WaitForMultipleObjects(
       2,            // number of handles in array
       hEvents,      // array of event handles
       TRUE,         // wait till all are signaled
       INFINITE);    // indefinite wait

  switch (dwWaitResult)
  {
       // Both event objects were signaled.
       case WAIT_OBJECT_0:
           // Read from the shared buffer.
           break;

       // An error occurred.
       default:
           printf("Wait error: %d\n", GetLastError());
           ExitThread(0);
  }

  // Set the read event to signaled.

  if (! SetEvent(hEvents[0]) )
  {
       // Error exit.
  }
}

AND

Dll Class Exports

개발/Windows 2006. 11. 24. 15:02

출처 : MSDN '97

INFO: __declspec(dllexport) Replaces __export in 32-bit VC++


Last reviewed: September 30, 1997

Article ID: Q107501


이 기사의 정보는 다음에 적용된다 :

Microsoft Visual C++ 32-bit Edition, versions 1.0, 2.0, 4.0, 5.0

SUMMARY(요약)

Windows compiler를 위해 Visual C++과 함께 제공되는 __export 키워드는 Microsoft Visual C++ 32비트 컴파일러에서는 퇴색되었다. 이것의 Win32를 위한 적절한 기능은 __declspec을 dllexport 속성과 함께 사용하면서 달성될 수 있다. 4.0 버전의 Visual C++ 32-bit edition에서는 __export(혹은 _export) 키워드를 포함하는 컴파일링 코드는 다음 warnig 중 하나를 일으킨다:

C4236: nonstandard extension used : '__export' is an obsolete keyword,

  see documentation for __declspec(dllexport)

  -또는-

  C4226: nonstandard extension used : '__export' is an obsolete

  keyword

__export(혹은 _export) 키워드를 포함하는 컴파일링 코드는 1.0이나 2.x버전의 32비트 Visual C++에서 C4226 warning을 일으킨다.

MORE INFORMATION

__declspec 생성자는 Microsfot Win32 Software Development Kit(SDK)에 제공되는 툴로서 지원되지 않는다.

DLL 소스 코드를 Windows에서 Win32로 포팅할 때, __export의 각 인스턴스를 __declspec(dllexport)의 인스턴스로 각각 대체한다. __declspec(dllexport) 키워드는 데이터, 함수, 클래스, 클래스 멤버 함수를 익스포트하는데 사용될 수 있다. 예를 들어 :

/* exported function */

  __declspec( dllexport ) void func();


  /* exported data */

  __declspec( dllexport ) int i;


  // exported class

  class __declspec( dllexport ) DLLClass

  {

     ...

  };


  class DLLClass

  {

  public:

      // exported member function

      __declspec( dllexport ) void MemberFunction( void );

  };


아래의 샘플 코드는 __declspec(dllexport)와 __declspec(dllimport) 저장 클래스 속성을 DLL과 EXE에서 상대적으로 사용함으로써 클래스와 클래스 멤버 함수를 익스포트하는 법을 설명한다.


Sample Code

  /* DLL Sample: TESTDLL.CPP

  /* Compile options needed: /D"_X86" /MT TESTDLL.CPP /link

  /*                  /DLL /OUT:testdll.dll /implib:testdll.lib

  */


  #include <stdio.h>


  class DLLClass

      {

      public:

       // exported member function

       __declspec( dllexport ) void functionA( void ) {

           printf("\nIn Function A of the exported function");

       }

      };


  // exported class

  class __declspec( dllexport) ExportDLLClass

      {

      public:

       void functionB(void) {

          printf("\nIn Function B of the exported class");

       }

      };


  // exported instance of the DLLClass

  __declspec(dllexport) DLLClass test;


  /* Source that calls the DLL Sample: CALLDLL.CPP

  /* Compile options needed: /D"_X86" /D"_CONSOLE" /ML CALLDLL.CPP

  /*                  TESTDLL.LIB

  */


  #include <stdio.h>


  class DLLClass

      {

      public:

       // imported member function

       __declspec( dllimport ) void functionA( void );

      };


  class __declspec( dllimport) ExportDLLClass

      {

      public:

       void functionB(void);

      };


  __declspec( dllimport ) DLLClass test;


  void main(void)

  {

     ExportDLLClass TestClass;


     test.functionA();

     TestClass.functionB();

  }


REFERENCES

익스포트에 대한 더 많은 정보를 원하면, 다음 단어로 Microsoft Knowledge Base에서 검색하라

__export and def and prolog and dllexport

dllexport와 dllimport 저장 클래스 속성을 사용해 Win32를 위한 DLL을 생성하는 데 대한 더 많은 정보를 원한다면, Visual C++ 32-bit Edition과 함게 제공되는 "Programming Techniques" 매뉴얼의 Chapter 4를 참조하거나, Visual C++ Books Online에서 dllexport나 dllimport, export를 검색해 보라. > Visual Workbench(역자주 : Visual Studio메뉴 의미하는 듯)에서 Help를 선택하고, Keyword Search를 고른다.


Additional query words: 8.00 9.00

Keywords : CLngIss kbfasttip

Version : WINDOWS NT:1.0,2.0,4.0,5.0;

Platform : NT WINDOWS

THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY.

Last reviewed: September 30, 1997

ⓒ 1998 Microsoft Corporation. All rights reserved. Terms of Use

AND

#define SAFE_DELETE(ptr) {if(ptr){ delete (ptr); (ptr)=NULL;}}
AND