Does anyone have a 64 bit atomic increment decrement?

Bare metal programming in PureBasic, for experienced users
User avatar
RichAlgeni
Addict
Addict
Posts: 914
Joined: Wed Sep 22, 2010 1:50 am
Location: Bradenton, FL

Does anyone have a 64 bit atomic increment decrement?

Post 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.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Does anyone have a 64 bit atomic increment decrement?

Post 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
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
RichAlgeni
Addict
Addict
Posts: 914
Joined: Wed Sep 22, 2010 1:50 am
Location: Bradenton, FL

Re: Does anyone have a 64 bit atomic increment decrement?

Post by RichAlgeni »

Always appreciate your assistance Wilbert!

You're work is most instructive and helpful!
User avatar
RichAlgeni
Addict
Addict
Posts: 914
Joined: Wed Sep 22, 2010 1:50 am
Location: Bradenton, FL

Re: Does anyone have a 64 bit atomic increment decrement?

Post 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.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Does anyone have a 64 bit atomic increment decrement?

Post 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
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
RichAlgeni
Addict
Addict
Posts: 914
Joined: Wed Sep 22, 2010 1:50 am
Location: Bradenton, FL

Re: Does anyone have a 64 bit atomic increment decrement?

Post 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.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Does anyone have a 64 bit atomic increment decrement?

Post 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.
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
RichAlgeni
Addict
Addict
Posts: 914
Joined: Wed Sep 22, 2010 1:50 am
Location: Bradenton, FL

Re: Does anyone have a 64 bit atomic increment decrement?

Post 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!!!
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Does anyone have a 64 bit atomic increment decrement?

Post 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)
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
RichAlgeni
Addict
Addict
Posts: 914
Joined: Wed Sep 22, 2010 1:50 am
Location: Bradenton, FL

Re: Does anyone have a 64 bit atomic increment decrement?

Post 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
Post Reply