Well, imagine:
- Process A checks the file and there is no master.
- Process B checks the file and there is no master.
- Process A writes a key to say that it is a master.
- Process A checks the key to ensure that it is still master.
- Process B writes a key to say that it is a master.
- Process B checks the key to ensure that it is still master
Now, both processes think that they are masters.
You could introduce a sleep after writing the key. In this case Process A would see that process B had overwritten the master key so it would demote itself to a slave. In most cases this would probably work. This technique fails when the master process dies without completing it's task. To guard against this you would need all the slave processes to monitor the master and go through the master election process if the master dies.
Using a lock file makes the whole thing much more simple.
The example here will allows one arbitrary process to become the master. Only one process can become a master because of the atomic nature of locking a file in write mode.
If the master process it killed off then a slave will automatically promote itself to master status and complete the work. Slaves will only exit when their file has been serviced which means that as long as there is at least one slave left the job will be completed.
For this example I have not added code to lock the INI file while writing - I've no reason to believe it is necessary for local INI files.
SingleStream.kix
Code:
Break ON
$=SetOption("Explicit","ON")
; NB Pass the parameter on the command line as "$sFILE"
Dim $iIsMaster
Dim $sIni,$sLatch,$sInput,$sUnique,$sFileSection
$sLatch=".\LatchFile.TXT"
$sIni=".\IPC.INI"
$sFileSection="FILELIST"
; Generate a unique key for IPC comms
$sUnique=CSTR(@PID)+"."+@DATE+"."+@TIME+"."+@MSECS+"."+@TICKS
If Not IsDefined($sFile)
"No parameter $$sFile passed on command line - aborting"+@CRLF
Exit 1
EndIf
; Write parameter to IPC
$=WriteProfileString($sIni,$sFileSection,$sUnique,$sFile)
; We will either become the master process, or we will wait for the master process to action our file
While ReadProfileString($sIni,$sFileSection,$sUnique)<>"" And Not $iIsMaster
If Not $iIsMaster
$iIsMaster=FreeFileHandle()
If Open($iIsMaster,$sLatch,1+4)
$iIsMaster=0
Sleep 0.1 ; Give the CPU a break!
Else
; Check again to avoid race conditions
If ReadProfileString($sIni,$sFileSection,$sUnique)<>""
"Process "+@PID+" has become the master, enter a string to continue: " GetS $sInput
Else
$=Close($iIsMaster)
$iIsMaster=0
EndIf
EndIf
EndIf
Loop
; If we are the master process then we need to action the file list, otherwise our job is done.
If $iIsMaster
For Each $sUnique in Split(ReadProfileString($sIni,$sFileSection,""),Chr(10))
If $sUnique
$sFile=ReadProfileString($sIni,$sFileSection,$sUnique)
"Slave process # "+Split($sUnique,".")[0]+" has asked master to deal with '"+$sFile+"'"+@CRLF
;
; ***************************
; * Do file processing here *
; ***************************
;
; Delete file key to release slave
$=WriteProfileString($sIni,$sFileSection,$sUnique,"")
EndIf
Next
"Hit return to exit: " GetS $
Else
"Slave process "+@PID+" released."+@CRLF
EndIf
; vim: ts=4 sw=4 fdc=4 fdm=marker ai
Test batch file:
SingleStreamTest.bat
Code:
Start "Single stream test file ONE" kix32.exe SingleStream.kix $sFile="This is file one"
Start "Single stream test file TWO" kix32.exe SingleStream.kix $sFile="This is file two"
Start "Single stream test file THREE" kix32.exe SingleStream.kix $sFile="This is file three"
Start "Single stream test file FOUR" kix32.exe SingleStream.kix $sFile="This is file four"