| ||
A critical section allows you to protect certain fragments of the program to ensure that only one thread is executed in this section at a time. Consider the functions for working with critical sections.
InitializeCriticalSection Creates an object called the critical section. It accepts the only parametera pointer to the CRITICAL_SECTION structure. The fields of this structure are used only by internal procedures; therefore, their meaning is of no importance for application programmers.
CRITICAL_SECTION STRUCT DebugInfo DWORD ? LockCount LONG ? RecursionCount LONG ? OwningThread HANDLE ? LockSemaphore HANDLE ? SpinCount DWORD ? CRITICAL_SECTION ENDS
EnterCriticalSection Enters the critical section. After executing this function, the current thread becomes the owner of the specified critical section. If another thread calls this function, it will enter the waiting state. This function accepts the same parameter as the previous one.
LeaveCriticalSection Leaves the critical section. After that, the second thread stopped by the EnterCriticalSection function will become the owner of the critical section. The LeaveCriticalSection function accepts the same parameter as the previous functions.
DeleteCriticalSection Deletes the critical section object. The parameter of this function is similar to the parameters of the previous functions.
Programmatically, it is possible to determine several critical section objects, with which several threads will operate . When describing critical sections, I intentionally mention only threads. Different processes cannot use this synchronization object.
Now, consider the example illustrating the use of critical section. The idea of the program presented in Listing 15.3 is as follows : Two threads from time to time call the procedure that displays the next character from the string into the window. As a result of such competing operation, the string must be printed. Part of the procedure that outputs the next character is made the critical section; therefore, only one thread at a time outputs data into the window.
; The THREAD2.INC file ; Constants ; This message arrives when the window is closed WM_DESTROY equ 2 ; This message arrives when the window is created WM_CREATE equ 1 ; This message arrives when the left mouse button is clicked ; in the window area WM_LBUTTONDOWN equ 201h ; This message arrives when the right mouse button is clicked ; in the window area WM_RBUTTONDOWN equ 204h ; Window properties CS_VREDRAW equ lh CS_HREDRAW equ 2h CS_GLOBALCLASS equ 4000h WS_OVERLAPPEDWINDOW equ 000CF0000H Stylcl equ CS_HREDRAW+CS_VREDRAW+CS_GLOBALCLASS DXO equ 300 DYO equ 200 ; Color components RED equ 50 GREEN equ 50 BLUE equ 255 RGBW equ (RED or (GREEN shl 8)) or (BLUE shl 16 RGBT equ 255 ; Red ; Standard icon identifier IDI_APPLICATION equ 32512 ; Cursor identifier IDC_CROSS equ 32515 ; Display mode---Normal SW_SHOWNORMAL equ 1 ; Prototypes of external procedures IFDEF MASM EXTERN Sleep@4:NEAR EXTERN CreateThread@24:NEAR EXTERN InitializeCriticalSection@4:NEAR EXTERN EnterCriticalSection@4:NEAR EXTERN LeaveCriticalSection@4:NEAR EXTERN DeleteCriticalSection@4:NEAR EXTERN GetTextExtentPoint32A@16:NEAR EXTERN CreateWindowExA@48:NEAR EXTERN DefWindowProcA@16:NEAR EXTERN DispatchMessageA@4:NEAR EXTERN ExitProcess@4:NEAR EXTERN GetMessageA@16:NEAR EXTERN GetModuleHandleA@4:NEAR EXTERN LoadCursorA@8:NEAR EXTERN LoadIconA@8:NEAR EXTERN PostQuitMessage@4:NEAR EXTERN RegisterClassA@4:NEAR EXTERN ShowWindow@8:NEAR EXTERN TranslateMessage@4: NEAR EXTERN UpdateWindow@4:NEAR EXTERN TextOutA@20:NEAR EXTERN CreateSolidBrush@4:NEAR EXTERN SetBkColor@8:NEAR EXTERN SetTextColor@8:NEAR EXTERN GetDC@4:NEAR EXTERN DeleteDC@4:NEAR ELSE EXTERN DeleteDC@4:NEAR EXTERN Sleep:NEAR EXTERN CreateThread:NEAR EXTERN InitializeCriticalSection:NEAR EXTERN EnterCriticalSection:NEAR EXTERN LeaveCriticalSection:NEAR EXTERN DeleteCriticalSection:NEAR EXTERN GetTextExtentPoint32A:NEAR EXTERN CreateWindowExA:NEAR EXTERN DefWindowProcA:NEAR EXTERN DispatchMessageA:NEAR EXTERN ExitProcess:NEAR EXTERN GetMessageA:NEAR EXTERN GetModuleHandleA:NEAR EXTERN LoadCursorA:NEAR EXTERN LoadlconA:NEAS EXTERN PostQuitMessage:NEAR EXTERN RegisterClassA:NEAR EXTERN ShowWindow:NEAR EXTERN TranslateMessage:NEAR EXTERN UpdateWindow:NEAR EXTERN TextOutA:NEAR EXTERN CreateSolidBrush:NEAR EXTERN SetBkColor:NEAR EXTERN SetTextColor:NEAR EXTERN GetDC:NEAR EXTERN DeleteDC:NEAR Sleep@4 = Sleep CreateThread@24 = CreateThread InitializeCriticalSection@4 = InitializeCriticalSection EnterCriticalSection@4 = EnterCriticalSection LeaveCriticalSection@4 = LeaveCriticalSection DeleteCriticalSection@4 = DeleteCriticalSection GetTextExtentPoint32A@16 = GetTextExtentPoint32A CreateWindowExA@48 = CreateWindowExA DefWindowProcA@16 = DefWindowProcA DispatchMessageA@4 = DispatchMessageA ExitProcess@4 = ExitProcess GetMessageA@16 = GetMessageA GetModuleHandleA@4 = GetModuleHandleA LoadCursorA@8 = Load-CursorA LoadIconA@8 = LoadIconA PostQuitMessage@4 = PostQuitMessage RegisterClassA@4 = RegisterClassA ShowWindow@8 = ShowWindow TranslateMessage@4 = TranslateMessage UpdateWindow@4 = UpdateWindow TextOutA@20 = TextOutA CreateSolidBrush@4 = CreateSolidBrush SetBkColor@8 = SetBkColor SetTextColor@8 = SetTextColor GetDC@4 = GetDC DeleteDC@4 = DeleteDC ENDIF ; Structures ; Message structure MSGSTRUCT STRUC MSHWND DD ? MSMESSAGE DD ? MSWPARAM DD ? MSLPARAM DD ? MSTIME DD ? MSPT DD ? MSGSTRUCT ENDS ;---------- WNDCLASS STRUC CLSSTYLE DD ? CLSLPFNWNDPROC DD ? CLSCBCLSEXTRA DD ? CLSCBWNDEXTRA DD ? CLSHINSTANCE DD ? CLSHICON DD ? CLSHCURSOR DD ? CLSHBRBACKGROUND DD ? MENNAME DD ? CLSNAME DD ? WNDCLASS ENDS ; Structure for working with the critical section CRIT STRUC DD ? DD ? DD ? DD ? DD ? DD ? CRIT ENDS ; Structure for determining the text length SIZET STRUC X1 DWORD ? Y1 DWORD ? SIZET ENDS ; The THREAD2.ASM file .586P ; Flat memory model .MODEL FLAT, stdcall ;----------------------------------------------- include thread2.inc ; INCLUDELIB directives IFDEF MASM includelib c:\masm32\lib\user32.lib includelib c:\masm32\lib\kernel32.lib includelib c:\masm32\lib\gdi32.lib ELSE includelib c:\tasm32\lib\import32.lib ENDIF ;----------------------------------------------- ; Data segment _DATA SEGMENT NEWHWND DD 0 MSG MSGSTRUCT <?> WC WNDCLASS <?> SZT SIZET <?> HINST DD 0 TITLENAME DB 'Output into the window using two threads', 0 NAM DB 'CLASS32', 0 XT DWORD 30 YT DWORD 30 HW DD ? DC DD ? TEXT DB 'Text in window is red', 0 SPA DB ' ' DB ' ', 0 IND DD 0 SK CRIT <?> THR1 DD ? THR2 DD ? FLAG1 DD 0 FLAG2 DD 0 _DATA ENDS ; Code segment _TEXT SEGMENT START: ; Get the application descriptor PUSH 0 CALL GetModuleHandleA@4 MOV [HINST], EAX REG_CLASS: ; Fill the window structure ; Style MOV [WC.CLSSTYLE], stylcl ; Message-handling procedure MOV [WC.CLSLPFNWNDPROC], OFFSET WNDPROC MOV [WC.CLSCBCLSEXTRA], 0 MOV [WC.CLSCBWNDEXTRA], 0 MOV EAX, [HINST] MOV [WC.CLSHINSTANCE], EAX ;---------- Window icon PUSH IDI_APPLICATION PUSH 0 CALL LoadIconA@8 MOV [WC.CLSHICON], EAX ;---------- Window cursor PUSH IDC_CROSS PUSH 0 CALL LoadCursorA@8 MOV [WC.CLSHCURSOR], EAX ;---------- PUSH RGBW ; Brush color CALL CreateSolidBrush@4 ; Create a brush MOV [WC.CLSHBRBACKGROUND], EAX MOV DWORD PTR [WC.MENNAME], 0 MOV DWORD PTR [WC.CLSNAME], OFFSET NAM PUSH OFFSET WC CALL RegisterClassA@4 ; Create a window of the registered class PUSH 0 PUSH [HINST] PUSH 0 PUSH 0 PUSH DY0 ; DYO - Window height PUSH DX0 ; DXO - Window width PUSH 100 ; Y-coordinate PUSH 100 ; X-coordinate PUSH WS_OVERLAPPEDWINDOW PUSH OFFSET TITLENAME ; Window name PUSH OFFSET NAM ; Class name PUSH 0 CALL CreateWindowExA@48 ; Check for an error CMP EAX, 0 JZ _ERR MOV [NEWHWND], EAX ; Window descriptor ;---------------------------------- PUSH SW_SHOWNORMAL PUSH [NEWHWND] CALL ShowWindow@8 ; Show the newly-created window ;---------------------------------- PUSH [NEWHWND] CALL UpdateWindow@4 ; Redraw the visible part of the window ; Message-processing loop MSG_LOOP: PUSH 0 PUSH 0 PUSH 0 PUSH OFFSET MSG CALL GetMessageA@16 CMP AX, 0 JE END_LOOP PUSH OFFSET MSG CALL TranslateMessage@4 PUSH OFFSET MSG CALL DispatchMessageA@4 JMP MSG_LOOP END_LOOP: ; Exit the program (close the process) PUSH [MSG.MSWPARAM] CALL ExitProcess@4 _ERR: JMP END_LOOP ;---------------------------------- ; Window procedure ; Position of parameters in the stack ; [EBP+014H] ; LPARAM ; [EBP+10H] ; WAPARAM ; [EBP+0CH] ; MES ; [EBP+8] ; HWND WNDPROC PROC PUSH EBP MOV EBP, ESP PUSH EBX PUSH ESI PUSH EDI CMP DWORD PTR [EBP+0CH], WM_DESTROY JE WMDESTROY CMP DWORD PTR [EBP+0CH], WM_CREATE JE WMCREATE CMP DWORD PTR [EBP+0CH], WM_LBUTTONDOWN JNE CONTIN ; Check the start flag CMP FLAG1, 0 JNE DEFWNDPROC MOV FLAG1, 1 ; Initialize pointers LEA EAX, TEXT MOV IND, EAX MOV XT, 30 ; Start the first thread PUSH OFFSET THR1 PUSH 0 PUSH EAX PUSH OFFSET THREAD1 PUSH 0 PUSH 0 CALL CreateThread@24 ; Start the second thread PUSH OFFSET THR2 PUSH 0 PUSH EAX PUSH OFFSET THREAD2 PUSH 0 PUSH 0 CALL CreateThread@24. JMP DEFWNDPROC CONTIN: CMP DWORD PTR [EBP+0CH], WM_RBUTTONDOWN JNE DEFWNDPROC ; Check the start flag CMP FLAG2, 0 JNE DEFWNDPROC MOV FLAG2, 1 ; Initialize pointers LEA EAX, SPA MOV IND, EAX MOV XT, 30 ; Start the first thread PUSH OFFSET THR1 PUSH 0 PUSH EAX PUSH OFFSET THREAD1 PUSH 0 PUSH 0 CALL CreateThread@24 ; Start the second thread PUSH OFFSET THR2 PUSH 0 PUSH EAX PUSH OFFSET THREAD2 PUSH 0 PUSH 0 CALL CreateThread@24 JMP DEFWNDPROC WMCREATE: MOV EAX, DWORD PTR [EBP+08H] ; Store the window descriptor in a global variable MOV HW, EAX ; Initialize the critical section PUSH OFFSET SK CALL InitializeCriticalSection@4 MOV EAX, 0 JMP FINISH DEFWNDPROC: PUSH DWORD PTR [EBP+14H] PUSH DWORD PTR [EBP+10H] PUSH DWORD PTR [EBP+0CH] PUSH DWORD PTR [EBP+08H] CALL DefWindowProcA@16 JMP FINISH WMDESTROY: ; Delete the critical section PUSH OFFSET SK CALL DeleteCriticalSection@4 PUSH 0 CALL PostQuitMessage@4 ; WM_QUIT MOV EAX, 0 FINISH: POP EDI POP ESI POP EBX POP EBP RET 16 WNDPROC ENDP ; Output OUTSTR PROC ; Check whether the text has finished MOV EBX, IND CMP BYTE PTR [EBX], 0 JNE NO_0 RET NO_0: ; Enter the critical section PUSH OFFSET SK CALL EnterCriticalSection@4 ;----------------- PUSH HW CALL GetDC@4 MOV DC, EAX ;----------------- Background color = Window color PUSH RGBW PUSH EAX CALL SetBkColor@8 ;----------------- Text color (red) PUSH RGBT PUSH DC CALL SetTextColor@8 ;----------------- Output the text PUSH 1 PUSH IND PUSH YT PUSH XT PUSH DC CALL TextOutA@20 ; Compute the text length in pixels PUSH OFFSET SZT PUSH 1 PUSH IND PUSH DC CALL GetTextExtentPoint32A@16 ; Increase the pointers MOV EAX, SZT.X1 ADD XT, EAX INC IND ;----------------- Close the context PUSH DC CALL DeleteDC@4 ; Exit the critical section PUSH OFFSET SK CALL LeaveCriticalSection@4 RET OUTSTR ENDP ; First thread THREAD1 PROC LO1: ; Check whether the end of the text has been reached MOV EBX, IND CMP BYTE PTR [EBX], 0 JE _END1 ; Output the next character CALL OUTSTR ; Delay PUSH 1000 CALL Sleep@4 JMP LO1 _END1: RET 4 THREAD1 ENDP ; Second thread THREAD2 PROC LO2: ; Check whether the end of the text has been reached MOV EBX, IND CMP BYTE PTR [EBX], 0 JE _END2 ; Output the next character CALL OUTSTR ; Delay PUSH 1000 CALL Sleep@4 JMP LO2 _END2: RET 4 THREAD2 ENDP _TEXT ENDS END START
To translate the THREAD2.ASM program presented in Listing 15.3, issue the following commands using MASM32:
ML /c /coff /DMASM thread2.asm LINK / SUBSYSTEM :WINDOWS thread2.obj
Issue the following commands using TASM32:
TASM32 /ml thread2.asm TLINK32 -aa thread2.obj
When the user clicks the left mouse button in the window area, the program starts string output. When the user clicks the right mouse button, the displayed string is erased. Flags FLAG1 and FLAG2 are introduced to ensure that string output and blank string output could be carried out only once. To slow text output, the delay ( Sleep ) is introduced into the loop for calling the OUTSTR procedure in each thread. Note that the letters mainly appear in pairs. This can be explained because when one of the threads outputs a character, the second thread is already waiting for its turn to output a character. Thus, as soon as the first thread leaves the critical section, the second thread immediately outputs the next character. After that, both threads are delayed (the Sleep function).
To complete the topic on critical sections, it is necessary to mention that this method of synchronization is the fastest . Drawbacks of this approach include the impossibility of several threads accessing the section simultaneously and the lack of special tools that allow you to count the number of attempts at accessing the resource.
| ||