📜 ⬆️ ⬇️

Atomic operations

Just the other day I was asked a question.

And why do we need the LOCK prefix, or its analogue InterlockedDecrement when calling the _LStrClr procedure from the System module. This procedure decrements the reference count of a string and, when zeroed, frees the memory previously occupied by the string.

The essence of the question was this - it is almost impossible to imagine a situation where a line will lose refs simultaneously from two threads, and therefore an atomic operation in this case is redundant.
')
In principle, the premise is interesting, but ...

But we are passing the string to the thread class.
This at least leads to an increase in refCnt, and therefore we can “get” on MemLeak if atomic operations were not used with the reference count decrement.

This shows us the _LStrClr code:

procedure _LStrClr(var S); {$IFDEF PUREPASCAL} var P: PStrRec; begin if Pointer(S) <> nil then begin P := Pointer(Integer(S) - Sizeof(StrRec)); Pointer(S) := nil; if P.refCnt > 0 then if InterlockedDecrement(P.refCnt) = 0 then FreeMem(P); end; end; {$ELSE} asm { -> EAX pointer to str } MOV EDX,[EAX] { fetch str } TEST EDX,EDX { if nil, nothing to do } JE @@done MOV dword ptr [EAX],0 { clear str } MOV ECX,[EDX-skew].StrRec.refCnt { fetch refCnt } DEC ECX { if < 0: literal str } JL @@done LOCK DEC [EDX-skew].StrRec.refCnt { threadsafe dec refCount } JNE @@done PUSH EAX LEA EAX,[EDX-skew].StrRec.refCnt { if refCnt now zero, deallocate} CALL _FreeMem POP EAX @@done: end; {$ENDIF} 


In the case of non-atomic decrement, the JNE instruction has a huge chance of being executed incorrectly. (And it really will fail if you remove the LOCK prefix).

Of course, I tried to explain this situation with examples from the Intel manual, where the work is explained, but in the end I decided to implement the following example (which I was able to convince the author of the question):

 program interlocked; {$APPTYPE CONSOLE} uses Windows; const Limit = 1000000; DoubleLimit = Limit shl 1; var SameGlobalVariable: Integer; function Test1(lpParam: Pointer): DWORD; stdcall; var I: Integer; begin for I := 0 to Limit - 1 do asm lea eax, SameGlobalVariable inc [eax] //   end; end; function Test2(lpParam: Pointer): DWORD; stdcall; var I: Integer; begin for I := 0 to Limit - 1 do asm lea eax, SameGlobalVariable lock inc [eax] //   end; end; var I: Integer; hThread: THandle; ThreadID: DWORD; begin //     SameGlobalVariable SameGlobalVariable := 0; hThread := CreateThread(nil, 0, @Test1, nil, 0, ThreadID); for I := 0 to Limit - 1 do asm lea eax, SameGlobalVariable inc [eax] //   end; WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); if SameGlobalVariable <> DoubleLimit then Writeln('Step one failed. Expected: ', DoubleLimit, ' but current: ', SameGlobalVariable); //     SameGlobalVariable SameGlobalVariable := 0; hThread := CreateThread(nil, 0, @Test2, nil, 0, ThreadID); for I := 0 to Limit - 1 do asm lea eax, SameGlobalVariable lock inc [eax] //   end; WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); if SameGlobalVariable <> DoubleLimit then Writeln('Step two failed. Expected: ', DoubleLimit, ' but current: ', SameGlobalVariable); Readln; end. 


The essence of the example is that there is a certain global variable SameGlobalVariable (it acts as a line reference count from the initial formulation of the problem) and changes its values ​​in the normal and atomic modes using the thread.

Here you can clearly see the differences between the two modes of operation.
In the console you will see something like the following:

 Step one failed. Expected: 2000000 but current: 1018924 

Errors in the second version of the implementation you will never see.

By the way, the first option can be used as a fairly good randomizer (of which I spoke in previous articles).

Summarizing:

Analysis of the source code of system modules Delphi and VCL in particular, can sometimes give you much more information than assumptions about how it actually works and that is a fact, but ...



No, this is not a fact, it is more than a fact - it was in fact

Source: https://habr.com/ru/post/181694/


All Articles