vfp2
TRANSCRIPT
-
7/29/2019 vfp2
1/2
25/01/13 Knowlbits
www.foxpert.com/knowlbits_200910_1.htm 1/3
2009-10Oct-26
Software Development & Consulting
MenuLocking done quickly
UPDATE
Joel Leach pointed out on Twitter that the following code accomplishes the
same even though Visual FoxPro's documentation states that SET REPROCESS
TO 0 SECONDS is an invalid statement:
LOCAL llLockSET REPROCESS TO 0 SECONDSllLock = RLOCK()UNLOCK
I'll leave the article on here since the low level approach might be useful for
something else, too.
The only possible approach for finding out whether you can lock a record is to
actually lock it. Aside from editing records there can be various reasons why you
need to lock records. For instance, one application I'm working on, uses locks in
a table to validate the number of seats that the application is licensed to. Every
instance tries to find an unlocked record, then locks the record and updates it
with some more details. If there's no unlocked record, the user ran out of
licenses. Another module determines all locked records and displays all recorded
information. This allows administrators and supporters to find out who is still in
the application.
A straight forward approach would be to scan through the table, lock each
record and continue until RLOCK() returns .T. Problems arise when the number
of work stations and licenses grows. The first record is way more likely to belocked than the last one. Hence, the more users are logged on the longer it
takes to launch the application for new users.
One approach I implemented in the past to get around this is to check for
random record numbers instead of scanning through the table. Just generate a
random number between 1 and RECCOUNT(). Navigate to the record, attempt
to lock it, repeat with a new record. Probably you want to limit the number of
attempts to something like two or three times the total number of records
before exiting the application. This approach distributes locked records equally in
the table. It's still slow, though.
Visual FoxPro never tries to lock a record just once (let me know if you
disagree with this statement). Instead Visual FoxPro uses the value of SETREPROCESS TO for a number of attempts. There are various options such as
locking for a number of seconds, a number of attempts or eternally until the
user cancels out. In the end, though, it all boils down to Visual FoxPro repeatedly
trying to lock the record until a termination condition is met.
SYS(3051) controls the interval between attempts. Possible values for this
function are limited, though. The interval can be anything between 100 ms and
one second. The default is 333 ms meaning that a value of 3 for SET
REPROCESS makes the app wait up to one second before RLOCK() returns .F.
If you want to check a huge number of records, you want to minimize this
time. The fastest possible setting in Visual FoxPro is
SET REPROCESS TO 1SYS(3051,100)
When you attempt to lock a record that is already locked, you will know after
100 ms, more or less. Sufficient when all you want is to save a record, way to
slow if you need to check 100 records to find an unlocked one. Even with this
fast configuration, Visual FoxPro still performs two attempts to lock the record.
Previous KnowlBits
February 2011 (2)
December 2010 (1)
October 2009 (2)
September 2009 (1)
August 2009 (4)
July 2009 (2)
June 2009 (2)
May 2009 (1)
April 2009 (1)
March 2009 (1)
August 2008 (1)
July 2008 (2)
May 2008 (1)
April 2008 (2)
January 2008 (2)
December 2007 (2)
November 2007 (2)
October 2007 (1)
September 2007 (1)
August 2007 (5)
July 2007 (4)
May 2007 (6)
March 2007 (3)
February 2007 (7)
January 2007 (6)
November 2006 (1)
October 2006 (3)
September 2006 (10)
June 2006 (2)
May 2006 (6)
April 2006 (1)
Search
Powered by Google
http://www.foxpert.com/knowlbits_200606.htmhttp://www.foxpert.com/knowlbits_200710.htmhttp://www.foxpert.com/knowlbits_200808.htmhttp://www.foxpert.com/knowlbits_200903.htmhttp://www.foxpert.com/knowlbits_200906.htmhttp://www.foxpert.com/knowlbits_200907.htmhttp://www.foxpert.com/knowlbits_200909.htmhttp://www.foxpert.com/knowlbits_200910.htmhttp://devcon.dfpug.de/http://www.google.com/http://www.foxpert.com/knowlbits_200604.htmhttp://www.foxpert.com/knowlbits_200605.htmhttp://www.foxpert.com/knowlbits_200606.htmhttp://www.foxpert.com/knowlbits_200609.htmhttp://www.foxpert.com/knowlbits_200610.htmhttp://www.foxpert.com/knowlbits_200611.htmhttp://www.foxpert.com/knowlbits_200701.htmhttp://www.foxpert.com/knowlbits_200702.htmhttp://www.foxpert.com/knowlbits_200703.htmhttp://www.foxpert.com/knowlbits_200705.htmhttp://www.foxpert.com/knowlbits_200707.htmhttp://www.foxpert.com/knowlbits_200708.htmhttp://www.foxpert.com/knowlbits_200709.htmhttp://www.foxpert.com/knowlbits_200710.htmhttp://www.foxpert.com/knowlbits_200711.htmhttp://www.foxpert.com/knowlbits_200712.htmhttp://www.foxpert.com/knowlbits_200801.htmhttp://www.foxpert.com/knowlbits_200804.htmhttp://www.foxpert.com/knowlbits_200805.htmhttp://www.foxpert.com/knowlbits_200807.htmhttp://www.foxpert.com/knowlbits_200808.htmhttp://www.foxpert.com/knowlbits_200903.htmhttp://www.foxpert.com/knowlbits_200904.htmhttp://www.foxpert.com/knowlbits_200905.htmhttp://www.foxpert.com/knowlbits_200906.htmhttp://www.foxpert.com/knowlbits_200907.htmhttp://www.foxpert.com/knowlbits_200908.htmhttp://www.foxpert.com/knowlbits_200909.htmhttp://www.foxpert.com/knowlbits_200910.htmhttp://www.foxpert.com/knowlbits_201012.htmhttp://www.foxpert.com/knowlbits_201102.htmhttp://www.foxpert.com/knowlbits.xmlhttp://twitter.com/joel_leachhttp://guineu.foxpert.com/http://www.foxpert.com/knowlbits.htmhttp://www.foxpert.com/downloads.htmhttp://www.foxpert.com/whitepapers.htm -
7/29/2019 vfp2
2/2
25/01/13 Knowlbits
www.foxpert.com/knowlbits_200910_1.htm 2/3
One that fails right away putting VFP into a wait state. The second one occurs
after the specified interval.
When I needed to process potentially more than 100 records recently, this
approach was unusable. Therefore I searched my blog for a solution and came
across the description ofhow locking works in Visual FoxPro.
Obviously, the problem in Visual FoxPro is not the attempt to lock the record,
but the interval between the two attempts that you can't make shorter than
100 ms. Therefore my solution works on the API level to only make a single
locking attempt. Either it fails, or doesn't. Calculating the locking position is fairly
easy to do. It's 0x7FFFFFFE-Recno(). For the LockFile function you need a
handle to the DBF file. Lacking an easy way of obtaining a file handle to a table
opened by VFP, I opted for the API solution of opening the file a second time in
shared mode. Having said this the code is really straight forward.
*==========================================================* Attempts to lock a record exactly once. If the record* cannot be locked for whatever reason, .F. is returned.* The record does not remain locked.** There's no guarantee that a subsequent RLOCK() command* works. It might fail even when isLocked() returns .T.* when another machine locked the record in the meantime.* It might had worked, if you tried to lock the record as* the other machine released the lock already.** This method is for code that needs to quickly scan a* larger number of records to find a locked or unlocked* one, for instance, when you need to clean certain* records, or when you use locked records in a license* tables to limit the number of instances.
*==========================================================Procedure LockingPossible()
*--------------------------------------------------------* If this instance has locked the record, we return* immediately.*--------------------------------------------------------If IsRLocked()Return .T.
EndIf
*--------------------------------------------------------
* We can't check exclusively opened files. But then there* shouldn't be a problem obtaining a lock.*--------------------------------------------------------If IsExclusive()Return .T.
EndIf
*--------------------------------------------------------* API declarations** source: http://www.news2news.com*--------------------------------------------------------DECLARE INTEGER LockFile IN kernel32;
INTEGER hFile, INTEGER dwFileOffsetLow,;INTEGER dwFileOffsetHigh,;INTEGER nNumberOfBytesToLockLow,;INTEGER nNumberOfBytesToLockHigh
DECLARE INTEGER OpenFile IN kernel32;STRING lpFileName,;STRING @ lpReOpenBuff,;INTEGER wStyle
DECLARE INTEGER CloseHandle IN kernel32;INTEGER hObject
*--------------------------------------------------------* Open the table a second time and attempt to lock the* record using low level access.*--------------------------------------------------------Local lcBuffer, lnFile, llLock#DEFINE GENERIC_READ 0x80000000llLock = .F.lcBuffer = Replicate(Chr(0), 250)lnFile = OpenFile(Dbf(), @lcBuffer, GENERIC_READ)If m.lnFile > 0If LockFile(m.lnFile, 0x7FFFFFFE-Recno(), 0, 1, 0) 0
llLock = .T.EndIfCloseHandle(m.lnFile)
EndIf
Return m.llLock
http://www.foxpert.com/knowlbits_200707_2.htm