1 / 21

Win32 Programming

Win32 Programming. Lesson 11: User-mode Thread Sync (aka: How to crash your machine without really trying…). Where are we?. We’ve got thread basics worked out… even priorities But right now, all the examples are sort-of contrived… Need to understand how to communicate from thread to thread.

afi
Download Presentation

Win32 Programming

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Win32 Programming Lesson 11: User-mode Thread Sync (aka: How to crash your machine without really trying…)

  2. Where are we? • We’ve got thread basics worked out… even priorities • But right now, all the examples are sort-of contrived… • Need to understand how to communicate from thread to thread

  3. Thread Problems • When we share data between threads, bad things can happen • Look at the following example code… • What’s wrong? • Can replace using “interlocked” functions

  4. InterlockedExchangeAdd • LONG InterlockedExchangeAdd ( PLONG plAddend, LONG lIncrement); • What could be simpler – promises Atomic access to *plAddend

  5. Family • LONG InterlockedExchange(     PLONG plTarget,      LONG lValue); • PVOID InterlockedExchangePointer(     PVOID* ppvTarget,      PVOID pvValue);

  6. Spinlock… • // Global variable indicating whether a shared resource is in use or not BOOL g_fResourceInUse = FALSE; void Func1() {     // Wait to access the resource.     while (InterlockedExchange ( &g_fResourceInUse,  TRUE) == TRUE)        Sleep(0);    // Access the resource.    // We no longer need to access the resource.    InterlockedExchange(&g_fResourceInUse, FALSE); }

  7. Spinlocks (cntd) • Be careful on a single processor machine • Is Sleep(0) the best call? Why?

  8. More Interlocked Calls… • PVOID InterlockedCompareExchange(     PLONG plDestination,      LONG lExchange,      LONG lComparand); • PVOID InterlockedCompareExchangePointer(     PVOID* ppvDestination,     PVOID pvExchange,      PVOID pvComparand);  • Basically, update on equality…

  9. Cashing in… (groan) • When a CPU accesses memory, it does not read a single byte, but instead fills a “cache line” (32 or 64 bytes, aligned on a 32 or 64 byte boundary) • If the cache becomes dirty it is flushed • Has a huge impact on how to design data structures for speed

  10. _declspec(align32) • See MSDN • Basically, forces alignment on a cache boundary • See: declspec example…

  11. DON’T DO THIS! • volatile BOOL g_fFinishedCalculation = FALSE; int WINAPI WinMain(...) {    CreateThread(..., RecalcFunc, ...);    // Wait for the recalculation to complete.    while (!g_fFinishedCalculation)         ;    }DWORD WINAPI RecalcFunc(PVOID pvParam) {    // Perform the recalculation.        g_fFinishedCalculation = TRUE;    return(0);}

  12. Volatile? • Well? • Oops (optimizer…): • ; Copy the value into a register Label: MOV   Reg0, [g_fFinishedCalculation]   TEST  Reg0, 0; Is the value 0? JMP   Reg0 == 0, Label; The register is 0, try again ...              ; The register is not 0 (end of loop)

  13. Critical Section • Can mark a code section as critical • This prevents any other thread accessing the resources within the critical section • Other threads do still get scheduled though…

  14. Look at this… • const int MAX_TIMES = 1000; int   g_nIndex = 0; DWORD g_dwTimes[MAX_TIMES]; DWORD WINAPI FirstThread(PVOID pvParam) {    while (g_nIndex < MAX_TIMES) {       g_dwTimes[g_nIndex] = GetTickCount();       g_nIndex++;    }    return(0);}DWORD WINAPI SecondThread(PVOID pvParam) {    while (g_nIndex < MAX_TIMES) {       g_nIndex++;       g_dwTimes[g_nIndex - 1] = GetTickCount();    }    return(0); }

  15. Fixed.. • const int MAX_TIMES = 1000; int   g_nIndex = 0; DWORD g_dwTimes[MAX_TIMES];CRITICAL_SECTION g_cs;DWORD WINAPI FirstThread(PVOID pvParam) {    while (g_nIndex < MAX_TIMES) {EnterCriticalSection(&g_cs);       g_dwTimes[g_nIndex] = GetTickCount();       g_nIndex++;LeaveCriticalSection(&g_cs);    }    return(0);}DWORD WINAPI SecondThread(PVOID pvParam) {    while (g_nIndex < MAX_TIMES) {EnterCriticalSection(&g_cs);       g_nIndex++;       g_dwTimes[g_nIndex - 1] = GetTickCount();LeaveCriticalSection(&g_cs);    }    return(0); }

  16. But… • You MUST remember to leave a critical section else no other thread can use the resource • And the Devil really is in the details

  17. Initialization • Before you can use a CRITICAL SECTION you must initialize it • VOID InitializeCriticalSection(PCRITICAL_SECTION pcs); • Results undefined if you don’t do this… • BTW, what do you notice about the return?

  18. Entering… • If no thread is using the resource, continue • Else put the calling thread into a WAIT state • VOID EnterCriticalSection(PCRITICAL_SECTION pcs);  • Can use: • BOOL TryEnterCriticalSection(PCRITICAL_SECTION pcs); • Don’t wait: return TRUE/FALSE

  19. Leaving • VOID LeaveCriticalSection(PCRITICAL_SECTION pcs);  • If we’re done with this Section check for any other threads waiting and mark 1 as schedulable • VOID DeleteCriticalSection(PCRITICAL_SECTION pcs);

  20. Tips • Use ONE critical section per shared resource • Access multiple resources using multiple critical sections • Don’t hold on to a critical section for a long time

  21. Next… • Thread Synchronization with Kernel objects

More Related