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

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

Embed Size (px)

Citation preview

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

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

Page 2: 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

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

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

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

InterlockedExchangeAdd LONG InterlockedExchangeAdd (

PLONG plAddend,LONG lIncrement

); What could be simpler – promises Atomic

access to *plAddend

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

Family LONG InterlockedExchange(    

PLONG plTarget,      LONG lValue);

PVOID InterlockedExchangePointer(     PVOID* ppvTarget,      PVOID pvValue);

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

Spinlock… // Global variable indicating whether a shared re

source 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); }

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

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

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

More Interlocked Calls… PVOID InterlockedCompareExchange(    

PLONG plDestination,      LONG lExchange,      LONG lComparand);

PVOID InterlockedCompareExchangePointer(     PVOID* ppvDestination,     PVOID pvExchange,      PVOID pvComparand); 

Basically, update on equality…

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

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

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

_declspec(align32) See MSDN Basically, forces alignment on a cache

boundary See: declspec example…

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

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);}

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

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)

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

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…

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

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); }

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

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); }

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

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

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

Initialization Before you can use a CRITICAL SECTION

you must initialize it VOID InitializeCriticalSection(PCRITICAL_SE

CTION pcs); Results undefined if you don’t do this… BTW, what do you notice about the return?

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

Entering… If no thread is using the resource, continue Else put the calling thread into a WAIT state VOID EnterCriticalSection(PCRITICAL_SE

CTION pcs);  Can use:

BOOL TryEnterCriticalSection(PCRITICAL_SECTION pcs);

Don’t wait: return TRUE/FALSE

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

Leaving VOID LeaveCriticalSection(PCRITICAL_SE

CTION pcs);  If we’re done with this Section check for any

other threads waiting and mark 1 as schedulable

VOID DeleteCriticalSection(PCRITICAL_SECTION pcs);

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

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

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

Next… Thread Synchronization with Kernel objects