Barriers - The runners example

Share your advanced PureBasic knowledge/code with the community.
User avatar
StarBootics
Addict
Addict
Posts: 984
Joined: Sun Jul 07, 2013 11:35 am
Location: Canada

Barriers - The runners example

Post by StarBootics »

Hello everyone,

A tiny example I have found on the French forum I have took time to re-write OOP style. The original example is from Microdevweb. (I hope you don't mind)

It's an example about synchronizing threads using a Double Barriers tactic.

I hope you will find this useful and/or instructive.

Best regards
StarBootics

Code: Select all

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; AUTOMATICALLY GENERATED CODE, DO NOT MODIFY
; UNLESS YOU REALLY, REALLY, REALLY MEAN IT !!
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; Code generated by : Dev-Object - V1.7.2
; Project name : Barriers - The runners example
; File name : Barriers - OOP.pb
; File Version : 1.0.0
; Programmation : OK
; Programmed by : StarBootics
; Creation Date : 19-03-2022
; Last update : 19-03-2022
; Coded for PureBasic : V6.00 Beta 5
; Platform : Windows, Linux, MacOS X
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; Programming Notes
;
; Based on Microdevweb's original example (French Forum)
;
; https://www.purebasic.fr/french/viewtopic.php?p=206088
;
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

DeclareModule Barriers
  
  Interface Barriers
    
    Wait()
    Free()
    
  EndInterface
  
  Declare.i New(ThreadCount.i)
  
EndDeclareModule

Module Barriers
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< Structure declaration <<<<<
  
  Structure Private_Members
    
    VirtualTable.i
    Barrier1.i
    Barrier2.i
    Mutex.i
    ThreadCount.i
    Counter.i
    
  EndStructure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The Wait operator <<<<<
  
  Procedure Wait(*This.Private_Members)
    
    LockMutex(*This\Mutex)
    *This\Counter + 1
    
    If *This\Counter = *This\ThreadCount ; If all Threads have arrived at barrier #1
      WaitSemaphore(*This\Barrier2)      ; Close Barrier #2
      SignalSemaphore(*This\Barrier1)    ; Open Barrier #1
    EndIf
    
    UnlockMutex(*This\Mutex)
    
    WaitSemaphore(*This\Barrier1); Close Barrier #1
    SignalSemaphore(*This\Barrier1); Open Barrier #1
    
    LockMutex(*This\Mutex)
    
    *This\Counter - 1
    
    If *This\Counter = 0 ; If all Threads have arrived at barrier #2
      WaitSemaphore(*This\Barrier1) ; Close Barrier #1
      SignalSemaphore(*This\Barrier2) ; Open Barrier #2
    EndIf
    
    UnlockMutex(*This\Mutex)
    
    WaitSemaphore(*This\Barrier2) ; Close Barrier #2
    SignalSemaphore(*This\Barrier2) ; Open Barrier #2
    
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The Destructor <<<<<
  
  Procedure Free(*This.Private_Members)
    
    FreeSemaphore(*This\Barrier1)
    FreeSemaphore(*This\Barrier2)
    FreeMutex(*This\Mutex)
    FreeStructure(*This)
    
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The Constructor <<<<<
  
  Procedure.i New(ThreadCount.i)
    
    *This.Private_Members = AllocateStructure(Private_Members)
    *This\VirtualTable = ?START_METHODS
    
    *This\Barrier1 = CreateSemaphore(0)
    *This\Barrier2 = CreateSemaphore(1)
    *This\Mutex = CreateMutex()
    *This\ThreadCount = ThreadCount
    *This\Counter = 0
    
    ProcedureReturn *This
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The Virtual Table Entries <<<<<
  
  DataSection
    START_METHODS:
    Data.i @Wait()
    Data.i @Free()
    END_METHODS:
  EndDataSection
  
EndModule

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Code generated in : 00.001 seconds (84000.00 lines/second) <<<<<
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

CompilerIf #PB_Compiler_IsMainFile
  
  DeclareModule Runner
    
    Interface Runner
      
      GetNumber.i()
      GetThreadID.i()
      GetCurrentStage.i()
      GetBarriers.i()
      SetNumber(Number.i)
      SetThreadID(ThreadID.i)
      SetCurrentStage(CurrentStage.i)
      SetBarriers(*Barriers.Barriers::Barriers)
      IncrementCurrentStage(Increment.i = 1)
      Free()
      
    EndInterface
    
    Declare.i New(Number.i = 0, ThreadID.i = 0, CurrentStage.i = 0, *Barriers.Barriers::Barriers = #Null)
    
  EndDeclareModule
  
  Module Runner
    
    ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    ; <<<<< Structure declaration <<<<<
    
    Structure Private_Members
      
      VirtualTable.i
      Number.i
      ThreadID.i
      CurrentStage.i ; Special : Increment
      *Barriers.Barriers::Barriers
      
    EndStructure
    
    ; <<<<<<<<<<<<<<<<<<<<<<<<<<<
    ; <<<<< The observators <<<<<
    
    Procedure.i GetNumber(*This.Private_Members)
      
      ProcedureReturn *This\Number
    EndProcedure
    
    Procedure.i GetThreadID(*This.Private_Members)
      
      ProcedureReturn *This\ThreadID
    EndProcedure
    
    Procedure.i GetCurrentStage(*This.Private_Members)
      
      ProcedureReturn *This\CurrentStage
    EndProcedure
    
    Procedure.i GetBarriers(*This.Private_Members)
      
      ProcedureReturn *This\Barriers
    EndProcedure
    
    ; <<<<<<<<<<<<<<<<<<<<<<<<
    ; <<<<< The mutators <<<<<
    
    Procedure SetNumber(*This.Private_Members, Number.i)
      
      *This\Number = Number
      
    EndProcedure
    
    Procedure SetThreadID(*This.Private_Members, ThreadID.i)
      
      *This\ThreadID = ThreadID
      
    EndProcedure
    
    Procedure SetCurrentStage(*This.Private_Members, CurrentStage.i)
      
      *This\CurrentStage = CurrentStage
      
    EndProcedure
    
    Procedure SetBarriers(*This.Private_Members, *Barriers.Barriers::Barriers)
      
      *This\Barriers = *Barriers
      
    EndProcedure
    
    ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    ; <<<<< The Special operator(s) <<<<<
    
    Procedure IncrementCurrentStage(*This.Private_Members, Increment.i = 1)
      
      *This\CurrentStage = *This\CurrentStage + Increment
      
    EndProcedure
    
    ; <<<<<<<<<<<<<<<<<<<<<<<<<<
    ; <<<<< The Destructor <<<<<
    
    Procedure Free(*This.Private_Members)
      
      FreeStructure(*This)
      
    EndProcedure
    
    ; <<<<<<<<<<<<<<<<<<<<<<<<<<<
    ; <<<<< The Constructor <<<<<
    
    Procedure.i New(Number.i = 0, ThreadID.i = 0, CurrentStage.i = 0, *Barriers.Barriers::Barriers = #Null)
      
      *This.Private_Members = AllocateStructure(Private_Members)
      *This\VirtualTable = ?START_METHODS
      
      *This\Number = Number
      *This\ThreadID = ThreadID
      *This\CurrentStage = CurrentStage
      *This\Barriers = *Barriers
      
      ProcedureReturn *This
    EndProcedure
    
    ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    ; <<<<< The Virtual Table Entries <<<<<
    
    DataSection
      START_METHODS:
      Data.i @GetNumber()
      Data.i @GetThreadID()
      Data.i @GetCurrentStage()
      Data.i @GetBarriers()
      Data.i @SetNumber()
      Data.i @SetThreadID()
      Data.i @SetCurrentStage()
      Data.i @SetBarriers()
      Data.i @IncrementCurrentStage()
      Data.i @Free()
      END_METHODS:
    EndDataSection
    
  EndModule
  
  DeclareModule Runners
    
    Interface Runners
      
      CreateThreads()
      WaitThreads()
      Free()
      
    EndInterface
    
    Declare.i New()
    
  EndDeclareModule
  
  Module Runners
    
    ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    ; <<<<< Constants declaration <<<<<
    
    #RUNNERS_MAX = 10
    #STAGE_MAX = 5
    
    ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    ; <<<<< Structure declaration <<<<<
    
    Structure Private_Members
      
      VirtualTable.i
      Barriers.Barriers::Barriers
      Runners.Runner::Runner[#RUNNERS_MAX]
      
    EndStructure
    
    ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    ; <<<<< The RunnerThread procedure <<<<<
    
    Procedure RunnerThread(*Runner.Runner::Runner)
      
      Debug "Runner " + Str(*Runner\GetNumber()) + " is running !"
      
      Repeat
        
        *Runner\IncrementCurrentStage()
        Delay(Random(2000, 500))
        Debug "Runner " + Str(*Runner\GetNumber()) + " has arrived on stage " + Str(*Runner\GetCurrentStage()) + " and wait !"
        *Barriers.Barriers::Barriers = *Runner\GetBarriers()
        *Barriers\Wait()
        Debug "Runner " + Str(*Runner\GetNumber()) + " make is critical work !"
        
      Until *Runner\GetCurrentStage() >= #STAGE_MAX
      
    EndProcedure
    
    ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    ; <<<<< The CreateThreads operator <<<<<
    
    Procedure CreateThreads(*This.Private_Members)
      
      For RunnersID = 0 To #RUNNERS_MAX - 1
        *This\Runners[RunnersID]\SetThreadID(CreateThread(@RunnerThread(), *This\Runners[RunnersID]))
      Next
      
    EndProcedure
    
    ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    ; <<<<< The WaitThreads operator <<<<<
    
    Procedure WaitThreads(*This.Private_Members)
      
      For RunnersID = 0 To #RUNNERS_MAX - 1
        WaitThread(*This\Runners[RunnersID]\GetThreadID())
      Next
      
    EndProcedure
    
    ; <<<<<<<<<<<<<<<<<<<<<<<<<<
    ; <<<<< The Destructor <<<<<
    
    Procedure Free(*This.Private_Members)
      
      If *This\Barriers <> #Null
        *This\Barriers\Free()
      EndIf
      
      For RunnersID = 0 To #RUNNERS_MAX - 1
        If *This\Runners[RunnersID] <> #Null
          *This\Runners[RunnersID]\Free()
        EndIf
      Next
      
      FreeStructure(*This)
      
    EndProcedure
    
    ; <<<<<<<<<<<<<<<<<<<<<<<<<<<
    ; <<<<< The Constructor <<<<<
    
    Procedure.i New()
      
      *This.Private_Members = AllocateStructure(Private_Members)
      *This\VirtualTable = ?START_METHODS
      
      *This\Barriers = Barriers::New(#RUNNERS_MAX)
      
      For RunnersID = 0 To #RUNNERS_MAX - 1
        *This\Runners[RunnersID] = Runner::New(RunnersID+1, 0, 0, *This\Barriers)
      Next
      
      ProcedureReturn *This
    EndProcedure
    
    ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    ; <<<<< The Virtual Table Entries <<<<<
    
    DataSection
      START_METHODS:
      Data.i @CreateThreads()
      Data.i @WaitThreads()
      Data.i @Free()
      END_METHODS:
    EndDataSection
    
  EndModule
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  
  CompilerIf #PB_Compiler_Thread = 0
    Debug "Compile this example with the ThreadSafe option activated !"
    End
  CompilerEndIf
  
  Runners.Runners::Runners = Runners::New()
  
  Runners\CreateThreads()
  Runners\WaitThreads()
  Runners\Free()
  
  Debug "END OF PROGRAM"
  End
  
CompilerEndIf

; <<<<<<<<<<<<<<<<<<<<<<<
; <<<<< END OF FILE <<<<<
; <<<<<<<<<<<<<<<<<<<<<<<
The Stone Age did not end due to a shortage of stones !
BarryG
Addict
Addict
Posts: 3322
Joined: Thu Apr 18, 2019 8:17 am

Re: Barriers - The runners example

Post by BarryG »

I have no need for this but thanks for taking the time to work on and post it. Maybe one day I'll need it, so it's good to have it sitting here.
Post Reply