win32 programming

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

Upload: afi

Post on 22-Feb-2016

75 views

Category:

Documents


0 download

DESCRIPTION

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. - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: Win32 Programming

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

Page 2: Win32 Programming

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

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

InterlockedExchangeAdd LONG InterlockedExchangeAdd (

PLONG plAddend,LONG lIncrement

); What could be simpler – promises Atomic

access to *plAddend

Page 5: Win32 Programming

Family LONG InterlockedExchange(    

PLONG plTarget,      LONG lValue);

PVOID InterlockedExchangePointer(     PVOID* ppvTarget,      PVOID pvValue);

Page 6: Win32 Programming

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

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

Page 8: Win32 Programming

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

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

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

boundary See: declspec example…

Page 11: Win32 Programming

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

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

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

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

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

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

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

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

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

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

Next… Thread Synchronization with Kernel objects