Iczer

Using Semaphores

16 posts in this topic

I need to synchronise 3 different scripts to work with one object - to make sure only one script is "active". I wanted to use Semaphores for that, but...

How i can move from $bGlobalBusyFlag to Semaphores in :

#include <WinAPI.au3>
#include <ButtonConstants.au3>
#include <EditConstants.au3>
#include <GUIConstantsEx.au3>
#include <StaticConstants.au3>
#include <WindowsConstants.au3>
#Region ### START Koda GUI section ### Form=
$Form1 = GUICreate("Form1", 623, 367, 192, 132)
$Group1 = GUICtrlCreateGroup("Group1", 8, 8, 185, 105)
$Label1 = GUICtrlCreateLabel("Label1", 24, 40, 141, 20)
GUICtrlCreateGroup("", -99, -99, 1, 1)
$Group2 = GUICtrlCreateGroup("Group2", 8, 128, 185, 105)
$Label2 = GUICtrlCreateLabel("Label2", 24, 160, 45, 20)
GUICtrlCreateGroup("", -99, -99, 1, 1)
$Group3 = GUICtrlCreateGroup("Group3", 8, 248, 185, 105)
$Label3 = GUICtrlCreateLabel("Label3", 24, 280, 45, 20)
GUICtrlCreateGroup("", -99, -99, 1, 1)
$Group4 = GUICtrlCreateGroup("joint output", 232, 8, 377, 345)
$Edit1 = GUICtrlCreateEdit("", 248, 32, 345, 305)
GUICtrlSetData(-1, "Edit1")
GUICtrlCreateGroup("", -99, -99, 1, 1)
GUISetState(@SW_SHOW)
#EndRegion ### END Koda GUI section ###

Global $bGlobalBusyFlag = False

GUICtrlSetBkColor($Group2, 0xffff00)
GUICtrlSetBkColor($Group3, 0xffff00)

AdlibRegister("Program_1",200)
AdlibRegister("Program_2",250)
AdlibRegister("Program_3",300)



While 1
    $nMsg = GUIGetMsg()
    Switch $nMsg
        Case $GUI_EVENT_CLOSE
            Exit

    EndSwitch
WEnd

AdlibUnRegister("Program_1")
AdlibUnRegister("Program_2")
AdlibUnRegister("Program_3")
Exit


Func Program_1()
    Local Static $t
    Local Static $tBusyTime, $tFreeTime
    Local Static $bBusyFlag = False, $bFreeFlag = False, $bWaitFlag = True

    If $bBusyFlag Then
        If TimerDiff($t) > $tBusyTime Then
            $bGlobalBusyFlag = False
            $bBusyFlag = False
            $bFreeFlag = True
        Else
            GUICtrlSetBkColor($Group1, 0xff0000) ; Red
        EndIf
    EndIf

    If $bWaitFlag Then
        If $bGlobalBusyFlag Then
            GUICtrlSetBkColor($Group1, 0xffff00) ; Yellow
        Else
            $bGlobalBusyFlag = True
            $bWaitFlag = False
            $bBusyFlag = True
            $tBusyTime = Random(888,3333,1)
            $tFreeTime = Random(3333,5555,1)
            $t = TimerInit()
        EndIf
    EndIf

    If $bFreeFlag Then
        If TimerDiff($t) > $tFreeTime Then
            $bWaitFlag = True
            $bFreeFlag = False
        Else
            GUICtrlSetBkColor($Group1, 0x00ff00) ; Green
        EndIf
    EndIf

EndFunc

Func Program_2()
    Local Static $t
    Local Static $tBusyTime, $tFreeTime
    Local Static $bBusyFlag = False, $bFreeFlag = False, $bWaitFlag = True

    If $bBusyFlag Then
        If TimerDiff($t) > $tBusyTime Then
            $bGlobalBusyFlag = False
            $bBusyFlag = False
            $bFreeFlag = True
        Else
            GUICtrlSetBkColor($Group2, 0xff0000) ; Red
        EndIf
    EndIf

    If $bWaitFlag Then
        If $bGlobalBusyFlag Then
            GUICtrlSetBkColor($Group2, 0xffff00) ; Yellow
        Else
            $bGlobalBusyFlag = True
            $bWaitFlag = False
            $bBusyFlag = True
            $tBusyTime = Random(888,3333,1)
            $tFreeTime = Random(3333,5555,1)
            $t = TimerInit()
        EndIf
    EndIf

    If $bFreeFlag Then
        If TimerDiff($t) > $tFreeTime Then
            $bWaitFlag = True
            $bFreeFlag = False
        Else
            GUICtrlSetBkColor($Group2, 0x00ff00) ; Green
        EndIf
    EndIf

EndFunc

Func Program_3()
    Local Static $t
    Local Static $tBusyTime, $tFreeTime
    Local Static $bBusyFlag = False, $bFreeFlag = False, $bWaitFlag = True

    If $bBusyFlag Then
        If TimerDiff($t) > $tBusyTime Then
            $bGlobalBusyFlag = False
            $bBusyFlag = False
            $bFreeFlag = True
        Else
            GUICtrlSetBkColor($Group3, 0xff0000) ; Red
        EndIf
    EndIf

    If $bWaitFlag Then
        If $bGlobalBusyFlag Then
            GUICtrlSetBkColor($Group3, 0xffff00) ; Yellow
        Else
            $bGlobalBusyFlag = True
            $bWaitFlag = False
            $bBusyFlag = True
            $tBusyTime = Random(888,3333,1)
            $tFreeTime = Random(3333,5555,1)
            $t = TimerInit()
        EndIf
    EndIf

    If $bFreeFlag Then
        If TimerDiff($t) > $tFreeTime Then
            $bWaitFlag = True
            $bFreeFlag = False
        Else
            GUICtrlSetBkColor($Group3, 0x00ff00) ; Green
        EndIf
    EndIf

EndFunc

"active" - red, waiting - yellow, free - green

Share this post


Link to post
Share on other sites



3 function instead 3 standalone compiled scripts - for simplicity. 

idea - is to make script(s)/functions use $bBusyFlag (Red) only one at time - while $bGlobalBusyFlag = False

Share this post


Link to post
Share on other sites

Those 3 functions only for colouring, nothing more. No special order for colouring.

Rules are simple:

if $bGlobalBusyFlag = False - work/busy (red) and set$bGlobalBusyFlag = True
    -after some random time - set $bGlobalBusyFlag = False and colour green
    -after some random time - going waiting mode (Yellow) and check $bGlobalBusyFlag
if $bGlobalBusyFlag = True - waiting (Yellow)
repeat

Share this post


Link to post
Share on other sites

Could someone rewrite functions : Semaphore_GetState(), Semaphore_SetFree() and Semaphore_SetBusy() to work with Semaphores/Events instead of global variable?

 

#include <WinAPI.au3>
#include <ButtonConstants.au3>
#include <EditConstants.au3>
#include <GUIConstantsEx.au3>
#include <StaticConstants.au3>
#include <WindowsConstants.au3>
#Region ### START Koda GUI section ### Form=
$Form1 = GUICreate("Form1", 623, 367, 192, 132)
$Group1 = GUICtrlCreateGroup("Group1", 8, 8, 185, 105)
$Label1 = GUICtrlCreateLabel("Label1", 24, 40, 141, 20)
GUICtrlCreateGroup("", -99, -99, 1, 1)
$Group2 = GUICtrlCreateGroup("Group2", 8, 128, 185, 105)
$Label2 = GUICtrlCreateLabel("Label2", 24, 160, 45, 20)
GUICtrlCreateGroup("", -99, -99, 1, 1)
$Group3 = GUICtrlCreateGroup("Group3", 8, 248, 185, 105)
$Label3 = GUICtrlCreateLabel("Label3", 24, 280, 45, 20)
GUICtrlCreateGroup("", -99, -99, 1, 1)
$Group4 = GUICtrlCreateGroup("joint output", 232, 8, 377, 345)
$Edit1 = GUICtrlCreateEdit("", 248, 32, 345, 305)
GUICtrlSetData(-1, "Edit1")
GUICtrlCreateGroup("", -99, -99, 1, 1)
GUISetState(@SW_SHOW)
#EndRegion ### END Koda GUI section ###

Global $bGlobalBusyFlag = False
Global $hSemaphore = _WinAPI_CreateSemaphore("Semaphore_Test", 1, 1)
Global $g_tEvents = DllStructCreate("handle Event[1];")
$g_tEvents.Event(1) = _WinAPI_CreateEvent(0, True, False)


GUICtrlSetBkColor($Group2, 0xffff00)
GUICtrlSetBkColor($Group3, 0xffff00)

AdlibRegister("Program_1",200)
AdlibRegister("Program_2",250)
AdlibRegister("Program_3",300)

While 1
    $nMsg = GUIGetMsg()
    Switch $nMsg
        Case $GUI_EVENT_CLOSE
            Exit

    EndSwitch
WEnd

AdlibUnRegister("Program_1")
AdlibUnRegister("Program_2")
AdlibUnRegister("Program_3")
_WinAPI_CloseHandle($hSemaphore)
_WinAPI_CloseHandle($g_tEvents.Event(1))
Exit

;==============================================================
Func Program_1()
    Local Static $t
    Local Static $tBusyTime, $tFreeTime
    Local Static $bBusyFlag = False, $bFreeFlag = False, $bWaitFlag = True

    If $bBusyFlag Then
        If TimerDiff($t) > $tBusyTime Then
            Semaphore_SetFree()
            $bBusyFlag = False
            $bFreeFlag = True
        Else
            GUICtrlSetBkColor($Group1, 0xff0000) ; Red
        EndIf
    EndIf

    If $bWaitFlag Then
        If Semaphore_GetState() Then
            GUICtrlSetBkColor($Group1, 0xffff00) ; Yellow
        Else
            Semaphore_SetBusy()
            $bWaitFlag = False
            $bBusyFlag = True
            $tBusyTime = Random(888,3333,1)
            $tFreeTime = Random(3333,5555,1)
            $t = TimerInit()
        EndIf
    EndIf

    If $bFreeFlag Then
        If TimerDiff($t) > $tFreeTime Then
            $bWaitFlag = True
            $bFreeFlag = False
        Else
            GUICtrlSetBkColor($Group1, 0x00ff00) ; Green
        EndIf
    EndIf

EndFunc
;==============================================================
Func Program_2()
    Local Static $t
    Local Static $tBusyTime, $tFreeTime
    Local Static $bBusyFlag = False, $bFreeFlag = False, $bWaitFlag = True

    If $bBusyFlag Then
        If TimerDiff($t) > $tBusyTime Then
            Semaphore_SetFree()
            $bBusyFlag = False
            $bFreeFlag = True
        Else
            GUICtrlSetBkColor($Group2, 0xff0000) ; Red
        EndIf
    EndIf

    If $bWaitFlag Then
        If Semaphore_GetState() Then
            GUICtrlSetBkColor($Group2, 0xffff00) ; Yellow
        Else
            Semaphore_SetBusy()
            $bWaitFlag = False
            $bBusyFlag = True
            $tBusyTime = Random(888,3333,1)
            $tFreeTime = Random(3333,5555,1)
            $t = TimerInit()
        EndIf
    EndIf

    If $bFreeFlag Then
        If TimerDiff($t) > $tFreeTime Then
            $bWaitFlag = True
            $bFreeFlag = False
        Else
            GUICtrlSetBkColor($Group2, 0x00ff00) ; Green
        EndIf
    EndIf

EndFunc
;==============================================================
Func Program_3()
    Local Static $t
    Local Static $tBusyTime, $tFreeTime
    Local Static $bBusyFlag = False, $bFreeFlag = False, $bWaitFlag = True

    If $bBusyFlag Then
        If TimerDiff($t) > $tBusyTime Then
            Semaphore_SetFree()
            $bBusyFlag = False
            $bFreeFlag = True
        Else
            GUICtrlSetBkColor($Group3, 0xff0000) ; Red
        EndIf
    EndIf

    If $bWaitFlag Then
        If Semaphore_GetState() Then
            GUICtrlSetBkColor($Group3, 0xffff00) ; Yellow
        Else
            Semaphore_SetBusy()
            $bWaitFlag = False
            $bBusyFlag = True
            $tBusyTime = Random(888,3333,1)
            $tFreeTime = Random(3333,5555,1)
            $t = TimerInit()
        EndIf
    EndIf

    If $bFreeFlag Then
        If TimerDiff($t) > $tFreeTime Then
            $bWaitFlag = True
            $bFreeFlag = False
        Else
            GUICtrlSetBkColor($Group3, 0x00ff00) ; Green
        EndIf
    EndIf

EndFunc
;==============================================================
Func Semaphore_GetState()
    ;If Not (_WinAPI_WaitForSingleObject( $g_tEvents.Event(1),0) = -1) Then Return True
    Return $bGlobalBusyFlag
EndFunc
;==============================================================
Func Semaphore_SetFree()
    ;_WinAPI_SetEvent ( $g_tEvents.Event(1) )
    $bGlobalBusyFlag = False
EndFunc
;==============================================================
Func Semaphore_SetBusy()
    ;_WinAPI_ResetEvent ( $g_tEvents.Event(1) )
    $bGlobalBusyFlag = True
EndFunc
;==============================================================

 

Share this post


Link to post
Share on other sites

well... bump.

Share this post


Link to post
Share on other sites

Semaphores still bother me - need help  with Semaphore_GetState(), Semaphore_SetFree() and Semaphore_SetBusy()  functions.

Share this post


Link to post
Share on other sites

Have you considered mutex? it is simpler

Share this post


Link to post
Share on other sites

Have you considered mutex? it is simpler

+1.  If you only need to guarantee one routine will write the variable at a time you only need a mutex.  A semaphore would be more appropriate to a reference counting situation.  In C based languages for protecting a single global integer variable I used InterlockedIncrement and InterlockedDecrement which could be used to set it to 0 or 1 as a boolean flag.

 

Share this post


Link to post
Share on other sites

#11 ·  Posted (edited)

You could write text to a file and have each script check the number currently in the file and act if its the scripts turn. You could also run a seperate fourth script to control the use of the other 3 scripts. Why not just use 1 script?

Edited by computergroove

Get Scite to add a popup when you use a 3rd party UDF -> http://www.autoitscript.com/autoit3/scite/docs/SciTE4AutoIt3/user-calltip-manager.html

Share this post


Link to post
Share on other sites

Indeed, with mutex its simpler (see working example), but for more complex cases than binary-like flag i wanted to understand Semaphores...
 

#include <WinAPIProc.au3>
#include <GUIConstantsEx.au3>

#Region ### START Koda GUI section ### Form=
$Form1 = GUICreate("Mutex Test", 201, 367, (@DesktopWidth-201)/2, (@DesktopHeight-367)/2)
$Group1 = GUICtrlCreateGroup("Group1", 8, 8, 185, 105)
GUICtrlCreateGroup("", -99, -99, 1, 1)
$Group2 = GUICtrlCreateGroup("Group2", 8, 128, 185, 105)
GUICtrlCreateGroup("", -99, -99, 1, 1)
$Group3 = GUICtrlCreateGroup("Group3", 8, 248, 185, 105)
GUICtrlCreateGroup("", -99, -99, 1, 1)
GUISetState(@SW_SHOW)
#EndRegion ### END Koda GUI section ###

Global $sMutex = "GlobalBusyFlag"
Global $hMutex = _WinAPI_CreateMutex($sMutex, False, 0)
FileCopy(@ScriptDir&"\Program.exe",@ScriptDir&"\1_Program.exe",1)
FileCopy(@ScriptDir&"\Program.exe",@ScriptDir&"\2_Program.exe",1)
FileCopy(@ScriptDir&"\Program.exe",@ScriptDir&"\3_Program.exe",1)

Run(@ScriptDir&"\1_Program.exe")
Run(@ScriptDir&"\2_Program.exe")
Run(@ScriptDir&"\3_Program.exe")

While 1
    $nMsg = GUIGetMsg()
    Switch $nMsg
        Case $GUI_EVENT_CLOSE
            ProcessClose("1_Program.exe")
            ProcessClose("2_Program.exe")
            ProcessClose("3_Program.exe")
            Sleep(333)
            FileDelete(@ScriptDir&"\1_Program.exe")
            FileDelete(@ScriptDir&"\2_Program.exe")
            FileDelete(@ScriptDir&"\3_Program.exe")
            ExitLoop

    EndSwitch
WEnd

 

#include <WinAPIProc.au3>
#include <WinAPI.au3>

While 1
    Sleep(Random(3333,5555))
    Program()
WEnd

;==============================================================
Func Program()
    Local $sMutex = "GlobalBusyFlag", $ctrlID
    Local $sNameNum = Number(StringMid(@ScriptName,1,1))
    Switch $sNameNum
        Case 1
            $ctrlID = "[CLASS:Button; INSTANCE:1]"
        Case 2
            $ctrlID = "[CLASS:Button; INSTANCE:3]"
        Case 3
            $ctrlID = "[CLASS:Button; INSTANCE:5]"
    EndSwitch
    ControlSetText("[TITLE:Mutex Test]", "", $ctrlID, $sNameNum & " Waiting", 1)
    Local $hMutex = _WinAPI_OpenMutex($sMutex)
    _WinAPI_WaitForSingleObject($hMutex)
    ControlSetText("[TITLE:Mutex Test]", "", $ctrlID, $sNameNum & " Busy", 1)
    Sleep(Random(3333,5555))
    _WinAPI_ReleaseMutex($hMutex)
    ControlSetText("[TITLE:Mutex Test]", "", $ctrlID, $sNameNum & " Free", 1)
EndFunc
;==============================================================

 

Share this post


Link to post
Share on other sites

Iczer,

Semaphores are used to share an object amoung a defined number of threads.  Thebasic principle is to decrement the count when a thread uses the object and increment the count when a thread releases tge object.  See https://msdn.microsoft.com/en-us/library/windows/desktop/ms685129(v=vs.85).aspx for additional info.

Kylomas


Forum Rules         Procedure for posting code

"I like pigs.  Dogs look up to us.  Cats look down on us.  Pigs treat us as equals."

- Sir Winston Churchill

Share this post


Link to post
Share on other sites

kylomas, True. But the threads can belong to different processes.

Iczer, Here is a simple example that demonstrates how to use semaphores. The semaphores ensures that only three windows are open at a time. The scripts must be compiled.

Parent.au3:

#include <WinAPIProc.au3>
#include <WinAPI.au3>

If Not @Compiled Then
  MsgBox($MB_SYSTEMMODAL, '', 'To run this script, you must first compile it and then run the (.exe) file.')
  Exit
EndIf

Local $hSemaphore = _WinAPI_CreateSemaphore('MySemaphore', 3, 3)

For $i = 1 To 10
  _WinAPI_WaitForSingleObject($hSemaphore)
  ShellExecute( "Child.exe", $i )
Next

_WinAPI_CloseHandle($hSemaphore)

Child.au3:

#include <WinAPIProc.au3>
#include <GUIConstantsEx.au3>

$hGui = GUICreate( "Child #" & $CmdLine[1], 250, 200 )
GUISetState()

While GUIGetMsg() <> $GUI_EVENT_CLOSE
WEnd

_WinAPI_ReleaseSemaphore( _WinAPI_OpenSemaphore( 'MySemaphore' ) )

 

Share this post


Link to post
Share on other sites

#15 ·  Posted (edited)

Years ago I wrote a Max Instance component for Delphi using semaphores.  Where it can get tricky is if the first or master instance that creates the semaphore is closed while some of the secondary instances are still open.  If the Master instance destroys the semaphore on close then a new instance can create the semaphore again and you could end up with more than the max number of instances intended.

It has been many years since I messed with it.  So I don't recall the best solution to that issue. Anyway, it is something to keep in mind when planning the interaction controls of the threads.

 

 

Edited by MilesAhead

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now