Win32 Programming

Download Report

Transcript 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
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
InterlockedExchangeAdd


LONG InterlockedExchangeAdd (
PLONG plAddend,
LONG lIncrement
);
What could be simpler – promises Atomic
access to *plAddend
Family


LONG InterlockedExchange(
PLONG plTarget,
LONG lValue
);
PVOID InterlockedExchangePointer(
PVOID* ppvTarget,
PVOID pvValue
);
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); }
Spinlocks (cntd)


Be careful on a single processor machine
Is Sleep(0) the best call? Why?
More Interlocked Calls…



PVOID InterlockedCompareExchange(
PLONG plDestination,
LONG lExchange,
LONG lComparand
);
PVOID InterlockedCompareExchangePointer(
PVOID* ppvDestination,
PVOID pvExchange,
PVOID pvComparand
);
Basically, update on equality…
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
_declspec(align32)



See MSDN
Basically, forces alignment on a cache
boundary
See: declspec example…
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);
}
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)
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…
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);
}
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);
}
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
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?
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_SE
CTION pcs);
Don’t wait: return TRUE/FALSE
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_SE
CTION pcs);
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
Next…

Thread Synchronization with Kernel objects