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