Page 1 of 1

ThreadSafe Counter as Object or Standard Call

Posted: Sat Feb 13, 2021 3:05 pm
by mk-soft
For example, if someone needs something like this to check the number of running threads.

Update v0.01.1

Code: Select all

;-TOP

; Comment : ThreadSafe Counter as Object or Standard Call
; Author  : mk-soft
; Version : v0.01.1
; Create  : 13.02.2021

DeclareModule Counter
  
  Interface iCounter
    Free(Force = #False)
    Incr()
    Decr()
    Get()
  EndInterface
  
  Declare New()
  
  ; For direct call without interface
  Declare Free(*this, Force = #False)
  Declare Incr(*this)
  Declare Decr(*this)
  Declare Get(*this)
  
EndDeclareModule

Module Counter
  
  Structure udtCounter
    *vTable
    counter.i
    mutex.i
  EndStructure
  
  Procedure New()
    Protected *mem.udtCounter
    *mem = AllocateStructure(udtCounter)
    If *mem
      *mem\vTable = ?vTable
      *mem\mutex = CreateMutex()
    EndIf
    ProcedureReturn *mem
  EndProcedure
  
  Procedure Free(*this.udtCounter, Force = #False)
    With *this
      LockMutex(\mutex)
      If Force Or \counter = 0
        UnlockMutex(\mutex)
        FreeMutex(\mutex)
        FreeStructure(*this)
        ProcedureReturn #True
      Else
        UnlockMutex(\mutex)
        ProcedureReturn #False
      EndIf
    EndWith
  EndProcedure
  
  Procedure Incr(*this.udtCounter)
    Protected r1
    With *this
      LockMutex(\mutex)
      \counter + 1
      r1 = \counter
      UnlockMutex(\mutex)
      ProcedureReturn r1
    EndWith
  EndProcedure
  
  Procedure Decr(*this.udtCounter)
    Protected r1
    With *this
      LockMutex(\mutex)
      If \counter
        \counter - 1
      EndIf
      r1 = \counter
      UnlockMutex(\mutex)
      ProcedureReturn r1
    EndWith
  EndProcedure
  
  Procedure Get(*this.udtCounter)
    Protected r1
    With *this
      LockMutex(\mutex)
      r1 = \counter
      UnlockMutex(\mutex)
      ProcedureReturn r1
    EndWith
  EndProcedure
  
  DataSection
    vTable:
    Data.i @Free()
    Data.i @Incr()
    Data.i @Decr()
    Data.i @Get()
  EndDataSection
  
EndModule

; ****

CompilerIf #PB_Compiler_IsMainFile
  
  CompilerIf Not #PB_Compiler_Thread 
    CompilerError "Use Compiler Option ThreadSafe!"
  CompilerEndIf
  
  EnableExplicit
  
  Global cntThreads.Counter::iCounter = Counter::New()
  
  Procedure thWork(Number)
    Debug "Start Thread Number " + Number
    Delay(Random(1000, 200))
    Debug "Stop Thread Number " + Number
    cntThreads\Decr()
  EndProcedure
  
  Define i, thread
  
  For i = 1 To 50
    cntThreads\Incr()
    thread = CreateThread(@thWork(), i)
    If Not thread
      cntThreads\Decr()
    EndIf
    Delay(10)
  Next
  
  While cntThreads\Get()
    Delay(100)
  Wend
  
  cntThreads\Free()
  
CompilerEndIf

Re: ThreadSafe Counter as Object or Standard Call

Posted: Sat Feb 13, 2021 5:06 pm
by User_Russian

Code: Select all

  Procedure Free(*this.udtCounter, Force = #False)
    With *this
      LockMutex(\mutex)
      If Force Or \counter = 0
        UnlockMutex(\mutex)    ; <- Add.
        FreeMutex(\mutex)
        FreeStructure(*this)
        ProcedureReturn #True
      Else
        UnlockMutex(\mutex)
        ProcedureReturn #False
      EndIf
    EndWith
  EndProcedure

Re: ThreadSafe Counter as Object or Standard Call

Posted: Sat Feb 13, 2021 6:38 pm
by mk-soft
Update v0.01.1
- Bugfix Free

Thank you,
I was of the opinion that this is not necessary with FreeMutex. But it is not.