Win 32 Programming Lesson 11 Usermode Thread Sync





















- Slides: 21

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

Where are we? o o o 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 o o When we share data between threads, bad things can happen Look at the following example code… What’s wrong? Can replace using “interlocked” functions

Interlocked. Exchange. Add o o LONG Interlocked. Exchange. Add ( PLONG pl. Addend, LONG l. Increment ); What could be simpler – promises Atomic access to *pl. Addend

Family o o LONG Interlocked. Exchange( PLONG pl. Target, LONG l. Value ); PVOID Interlocked. Exchange. Pointer( PVOID* ppv. Target, PVOID pv. Value );

Spinlock… o // Global variable indicating whether a shared re source is in use or not BOOL g_f. Resource. In. Use = FALSE; void Func 1() { // Wait to access the resource. while (Interlocked. Exchange ( &g_f. Resource. In. Use, TRUE) == TRUE) Sleep(0); // Access the resource. // We no longer need to access the resource. Interlocked. Exchange(&g_f. Resource. In. Use, FALSE); }

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

More Interlocked Calls… o o o PVOID Interlocked. Compare. Exchange( PLONG pl. Destination, LONG l. Exchange, LONG l. Comparand ); PVOID Interlocked. Compare. Exchange. Pointer( PVOID* ppv. Destination, PVOID pv. Exchange, PVOID pv. Comparand ); Basically, update on equality…

Cashing in… (groan) o o o 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(align 32) o o o See MSDN Basically, forces alignment on a cache boundary See: declspec example…

DON’T DO THIS! o volatile BOOL g_f. Finished. Calculation = FALSE; int WINAPI Win. Main(. . . ) { Create. Thread(. . . , Recalc. Func, . . . ); // Wait for the recalculation to complete. while (!g_f. Finished. Calculation) ; } DWORD WINAPI Recalc. Func(PVOID pv. Param) { // Perform the recalculation. g_f. Finished. Calculation = TRUE; return(0); }

Volatile? o o Well? Oops (optimizer…): n ; Copy the value into a register Label: MOV Reg 0, [g_f. Finished. Calculation] TEST Reg 0, 0; Is the value 0? JMP Reg 0 == 0, Label; The register is 0, try again. . . ; The register is not 0 (end of loop)

Critical Section o o o 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… o const int MAX_TIMES = 1000; int g_n. Index = 0; DWORD g_dw. Times[MAX_TIMES]; DWORD WINAPI First. Thread(PVOID pv. Param) { while (g_n. Index < MAX_TIMES) { g_dw. Times[g_n. Index] = Get. Tick. Count(); g_n. Index++; } return(0); } DWORD WINAPI Second. Thread(PVOID pv. Param) { while (g_n. Index < MAX_TIMES) { g_n. Index++; g_dw. Times[g_n. Index - 1] = Get. Tick. Count(); } return(0); }

Fixed. . o const int MAX_TIMES = 1000; int g_n. Index = 0; DWORD g_dw. Times[MAX_TIMES]; CRITICAL_SECTION g_cs; DWORD WINAPI First. Thread(PVOID pv. Param) { while (g_n. Index < MAX_TIMES) { Enter. Critical. Section(&g_cs); g_dw. Times[g_n. Index] = Get. Tick. Count(); g_n. Index++; Leave. Critical. Section(&g_cs); } return(0); } DWORD WINAPI Second. Thread(PVOID pv. Param) { while (g_n. Index < MAX_TIMES) { Enter. Critical. Section(&g_cs); g_n. Index++; g_dw. Times[g_n. Index - 1] = Get. Tick. Count(); Leave. Critical. Section(&g_cs); } return(0); }

But… o o 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 o Before you can use a CRITICAL SECTION you must initialize it n n n VOID Initialize. Critical. Section(PCRITICAL_SE CTION pcs); Results undefined if you don’t do this… BTW, what do you notice about the return?

Entering… o o If no thread is using the resource, continue Else put the calling thread into a WAIT state VOID Enter. Critical. Section(PCRITICAL_SE CTION pcs); Can use: n n BOOL Try. Enter. Critical. Section(PCRITICAL_SE CTION pcs); Don’t wait: return TRUE/FALSE

Leaving o o o VOID Leave. Critical. Section(PCRITICAL_SE CTION pcs); If we’re done with this Section check for any other threads waiting and mark 1 as schedulable VOID Delete. Critical. Section(PCRITICAL_SE CTION pcs);

Tips o o o 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… o Thread Synchronization with Kernel objects