Page 1 of 1
Does anyone have a 64 bit atomic increment decrement?
Posted: Thu Mar 15, 2018 12:05 am
by RichAlgeni
For integers in Windows?
The intrinsic does not seem to work. The SDK kernel32.lib does not seem to export the function, and I cannot find it in kernel32.dll.
There is some assembler listed in other posts, but it does not appear to work.
Re: Does anyone have a 64 bit atomic increment decrement?
Posted: Thu Mar 15, 2018 7:07 am
by wilbert
Code: Select all
Procedure.q AtomicIncrement64(*Var64)
CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
!mov rdx, [p.p_Var64]
!mov rax, 1
!lock xadd [rdx], rax
!add rax, 1
CompilerElse
; store non-volatile registers
!mov [esp - 4], ebx
!mov [esp - 8], edi
; load 64 bit value into edx:eax
!mov edi, [p.p_Var64]
!mov eax, [edi]
!mov edx, [edi + 4]
; 64 bit atomic increment
!.l:
!mov ebx, 1
!mov ecx, 0
!add ebx, eax
!adc ecx, edx
!lock cmpxchg8b [edi]
!jnz .l
!mov eax, ebx
!mov edx, ecx
; restore non-volatile registers
!mov edi, [esp - 8]
!mov ebx, [esp - 4]
CompilerEndIf
ProcedureReturn
EndProcedure
Procedure.q AtomicDecrement64(*Var64)
CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
!mov rdx, [p.p_Var64]
!mov rax, -1
!lock xadd [rdx], rax
!add rax, -1
CompilerElse
; store non-volatile registers
!mov [esp - 4], ebx
!mov [esp - 8], edi
; load 64 bit value into edx:eax
!mov edi, [p.p_Var64]
!mov eax, [edi]
!mov edx, [edi + 4]
; 64 bit atomic decrement
!.l:
!mov ebx, -1
!mov ecx, -1
!add ebx, eax
!adc ecx, edx
!lock cmpxchg8b [edi]
!jnz .l
!mov eax, ebx
!mov edx, ecx
; restore non-volatile registers
!mov edi, [esp - 8]
!mov ebx, [esp - 4]
CompilerEndIf
ProcedureReturn
EndProcedure
A.q = 5
For i = 1 To 10
Debug AtomicIncrement64(@A)
Next
It's also possible to use one Add procedure and use -1 for decrement.
Code: Select all
Procedure.q AtomicAdd64(*Var64, Value64.q)
CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
!mov rdx, [p.p_Var64]
!mov rax, [p.v_Value64]
!lock xadd [rdx], rax
!add rax, [p.v_Value64]
CompilerElse
; store non-volatile registers
!mov [esp - 4], ebx
!mov [esp - 8], edi
; load 64 bit value into edx:eax
!mov edi, [p.p_Var64]
!mov eax, [edi]
!mov edx, [edi + 4]
; 64 bit atomic add
!.l:
!mov ebx, [p.v_Value64]
!mov ecx, [p.v_Value64 + 4]
!add ebx, eax
!adc ecx, edx
!lock cmpxchg8b [edi]
!jnz .l
!mov eax, ebx
!mov edx, ecx
; restore non-volatile registers
!mov edi, [esp - 8]
!mov ebx, [esp - 4]
CompilerEndIf
ProcedureReturn
EndProcedure
A.q = 5
For i = 1 To 10
Debug AtomicAdd64(@A, -1)
Next
For i = 1 To 6
Debug AtomicAdd64(@A, 1)
Next
Re: Does anyone have a 64 bit atomic increment decrement?
Posted: Thu Mar 15, 2018 11:20 pm
by RichAlgeni
Always appreciate your assistance Wilbert!
You're work is most instructive and helpful!
Re: Does anyone have a 64 bit atomic increment decrement?
Posted: Fri Mar 16, 2018 2:20 am
by RichAlgeni
I'm sorry I have to ask this, but...
Could you write an Assembler process to force the variable (quad integer pointer) back to 0?
I am getting error C0000264, STATUS_RESOURCE_NOT_OWNED arbitrarily in this code:
Code: Select all
While Not TryAcquireSRWLockExclusive(*updateLock)
Delay(20)
Wend
AddElement(updateMessageLog())
updateMessageLog()\messageLogKey = *messageData\messageKey
updateMessageLog()\messageLogTyp = #snpp
updateMessageLog()\messageResult = result
ReleaseSRWLockExclusive(*updateLock)
Which seems impossible as the pointer used in the lock is not accessed in any other part of the program.
My thought is that I could use it as my own lock process.
Re: Does anyone have a 64 bit atomic increment decrement?
Posted: Fri Mar 16, 2018 6:43 am
by wilbert
RichAlgeni wrote:Could you write an Assembler process to force the variable (quad integer pointer) back to 0?
I'm not exactly sure if I understand what you want.
Do you want a procedure to set the value like the code below or do you need something else ?
Code: Select all
Procedure.q AtomicSetValue64(*Var64, Value64.q)
; the return value is the old value of *Var64
CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
!mov rdx, [p.p_Var64]
!mov rax, [p.v_Value64]
!lock xchg [rdx], rax
CompilerElse
; store non-volatile registers
!mov [esp - 4], ebx
!mov [esp - 8], edi
; load current value into edx:eax
!mov edi, [p.p_Var64]
!mov eax, [edi]
!mov edx, [edi + 4]
; 64 bit atomic exchange
!.l:
!mov ebx, [p.v_Value64]
!mov ecx, [p.v_Value64 + 4]
!lock cmpxchg8b [edi]
!jnz .l
; restore non-volatile registers
!mov edi, [esp - 8]
!mov ebx, [esp - 4]
CompilerEndIf
ProcedureReturn
EndProcedure
Procedure.q AtomicAdd64(*Var64, Value64.q)
CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
!mov rdx, [p.p_Var64]
!mov rax, [p.v_Value64]
!lock xadd [rdx], rax
!add rax, [p.v_Value64]
CompilerElse
; store non-volatile registers
!mov [esp - 4], ebx
!mov [esp - 8], edi
; load current value into edx:eax
!mov edi, [p.p_Var64]
!mov eax, [edi]
!mov edx, [edi + 4]
; 64 bit atomic add
!.l:
!mov ebx, [p.v_Value64]
!mov ecx, [p.v_Value64 + 4]
!add ebx, eax
!adc ecx, edx
!lock cmpxchg8b [edi]
!jnz .l
!mov eax, ebx
!mov edx, ecx
; restore non-volatile registers
!mov edi, [esp - 8]
!mov ebx, [esp - 4]
CompilerEndIf
ProcedureReturn
EndProcedure
A.q = 5
For i = 1 To 10
Debug AtomicAdd64(@A, 1)
Next
AtomicSetValue64(@A, 0)
For i = 1 To 10
Debug AtomicAdd64(@A, -1)
Next
Re: Does anyone have a 64 bit atomic increment decrement?
Posted: Fri Mar 16, 2018 3:23 pm
by RichAlgeni
Let me explain my thought process, and see if it makes sense to you.
1. The initial value of the Global integer will be 0.
2. Threads created will use AtomicIncrement64() to increment the value, when work is required.
3. Only when the integer returned to a thread is 1 will the thread be considered to be granted access to a locked resource.
4. Any other value returned (2, 3, etc.) and the thread will wait a period of time, then proceed to step 2.
5. The thread that does the work will use AtomicSetValue64() to set the value of the integer back to 0 when the work is complete, indicating that the resource is unlocked and available to another thread to process work needed on the resource.
6. The thread that did the work will proceed to step 2.
Re: Does anyone have a 64 bit atomic increment decrement?
Posted: Fri Mar 16, 2018 4:45 pm
by wilbert
Sounds logical
You could also consider a toggle bit; not sure what would work best.
Code: Select all
Procedure AtomicBitSet(*Mem, BitIdx.a = 0)
; the return value is the old bit value
!movzx eax, byte [p.v_BitIdx]
CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
!mov rdx, [p.p_Mem]
!lock bts [rdx], eax
CompilerElse
!mov edx, [p.p_Mem]
!lock bts [edx], eax
CompilerEndIf
!setc al
ProcedureReturn
EndProcedure
Procedure AtomicBitReset(*Mem, BitIdx.a = 0)
; the return value is the old bit value
!movzx eax, byte [p.v_BitIdx]
CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
!mov rdx, [p.p_Mem]
!lock btr [rdx], eax
CompilerElse
!mov edx, [p.p_Mem]
!lock btr [edx], eax
CompilerEndIf
!setc al
ProcedureReturn
EndProcedure
Global Flags = 0
Procedure MyThreadProc(Value)
Repeat
While AtomicBitSet(@Flags)
Delay(20)
Wend
For i = 1 To 10
Debug "Thread "+Str(Value)+" : "+Str(i)
Delay(100)
Next
AtomicBitReset(@Flags)
Delay(100)
ForEver
EndProcedure
CreateThread(@MyThreadProc(), 1)
CreateThread(@MyThreadProc(), 2)
Delay(10000)
AtomicBitSet sets bit 0 of the Flags value always to 1.
If it previously was 0, you can proceed. If not, you can consider it locked.
At the end of the procedure you can reset the bit to indicate it is no longer locked.
Re: Does anyone have a 64 bit atomic increment decrement?
Posted: Fri Mar 16, 2018 10:50 pm
by RichAlgeni
Ok, so we check the returned value, to see if the bit was changed by AtomicBitSet().
If it was, we are free to proceed with the locked resource.
Brilliant! Thanks so much Wilbert!!!
Re: Does anyone have a 64 bit atomic increment decrement?
Posted: Sat Mar 17, 2018 6:54 am
by wilbert
RichAlgeni wrote:Ok, so we check the returned value, to see if the bit was changed by AtomicBitSet().
If it was, we are free to proceed with the locked resource.
Yes, if it previously was 0, it has changed from 0 to 1 so a lock was acquired.
If you need to protect different resources, you can use a different bit index value.
With a quad value, you can protect 64 different resources.
The procedure itself supports a bit index from 0 - 255. If you want to use bit index 64 - 255, you can do it like this.
Code: Select all
Global *LockBits = AllocateMemory(32); allocate 256 bits
AtomicBitSet(*LockBits, 200)
AtomicBitReset(*LockBits, 200)
Re: Does anyone have a 64 bit atomic increment decrement?
Posted: Sat Mar 17, 2018 8:49 pm
by RichAlgeni
Brilliant! Thanks so much Wil!!!
I have 9 Windows Services programs that I am going to replace Slim Reader Writer with this.
I had high hopes for SRW. I don't understand how it could randomly fail like that.
So be it!
Thanks again!
Rich