What is a Semaphore Lock?
A long time ago I fell over the concept of signaling a lock across a network without actually locking a file or record. I forget the programming language, but thought how useful that would be in some of the programs I was developing at the time.
image courtesy of killrbeez
It is sometimes useful be able to indicate when a process is in progress without explicitly locking files or records that it relies on. For example, let’s assume that you wish to import a file, but that you need to ensure that no other user is attempting to import the same file.
Although there are any number of ways to accommodate this need, the one I favour is the use of a table whose sole reason for existence is to allow us to keep track of the process in question. The table I use is called ‘semaphore.dbf’ and has a single 20 character field called key. This field carries one index tag called key, which is based on the upper case value of the field content.
Index on UPPER(key) tag key
I have also written a small function (usually contained within the database stored procedures) that I invoke whenever I wish to apply a semaphore lock for a specified form or process.
While this particular piece of code does in fact utilise a record lock, it has proved its weight in gold in many recent projects.
*************************************
* Place a lock on a file or process *
* without actually locking them *
* Requires the table 'semaphore.dbf'*
*************************************
FUNCTION Semaphore
LPARAMETERS cOpt,cKey
LOCAL lcLockfile,llReturn,lnArea
lnArea=SELECT(0) && Where are we
cOpt=IIF(EMPTY(cOpt),"",LOWER(ALLTRIM(cOpt)))
cKey=IIF(EMPTY(cKey),"",UPPER(ALLTRIM(cKey)))
lcLockfile="semaphore" && Replace this with your table name
IF NOT USED(lcLockfile) && If not Open
USE (lcLockfile) IN 0 && Open and leave open
ENDIF
llReturn=.T.
DO CASE
CASE "clear"$cOpt OR EMPTY(ckey) && Try and remove any semaphore locks
SELECT (lcLockfile)
SCAN
IF RLOCK(lcLockfile)
DELETE
ENDIF
ENDSCAN
UNLOCK IN (lcLockfile)
CASE "unlock"$cOpt
IF SEEK(cKey,lcLockfile,"key")
DELETE IN (lcLockfile) && Self maintaining
ENDIF
UNLOCK IN (lcLockfile)
OTHERWISE
DO CASE
CASE NOT SEEK(cKey,lcLockfile,"key")
INSERT INTO (lcLockfile) (KEY) VALUES (cKey)
IF NOT RLOCK(lcLockfile)
llReturn=.F.
ENDIF
CASE NOT RLOCK(lcLockfile)
llReturn=.F.
ENDCASE
ENDCASE
SELECT (lnArea)
RETURN llReturn
ENDFUNC
The code below serves as an example of how to invoke and clear the lock:
do case
case not semaphore('lock','uniquekey') && apply lock using this key
=messagebox('process is already in progress',16,'Try again later')
otherwise
<do whatever process required>
=semaphore('unlock','uniquekey') && release specified semaphore
=semaphore('clear') && Release multiple locks if possible
endcase