home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD1.mdf
/
lan
/
critical
/
read.me
< prev
Wrap
Text File
|
1988-07-15
|
10KB
|
246 lines
This is a set of routines to provide a method of synchronizaton between
multiple workstations running Clipper programs simultaneously (for programs
compiled with the Summer '87 version).
I developed these routines to allow easy creation of data entry routines
that will handle multiple operators working on the same data file. For
example, lets say we are writing a program that will be used to log orders
into a production order file. Each production order has a unique number to
help track it through the plant.
The problem comes when two operators choose the "Add a Job" section at the
same time. I don't want to set it up so one operator is in the file
exclusively, because the purpose is to allow as many operators as possible.
But, I want to make sure that only one person at a time is getting the next
available number, incrementing it, and saving it back to the data file.
An example of code that contains the possibility of error is as follows
(This example assumes that we are running a multi-user situation, note the
use of already existing CLIPPER syntax to support network operations):
PROCEDURE addjob
*
* Declare the private variables (mshopord is where we hold the job number)
PRIVATE aborted,continue,mshopord
SET EXCLUSIVE OFF
aborted = .F.
continue = .T.
*
* Set up database file
USE jobs
DO WHILE NETERR() .AND. .NOT. aborted
@ 23,0 CLEAR
@ 23,0 SAY "File is currently locked. Wait 5 seconds or press spacebar to retry."
@ 24,0 SAY "Or, press the 'Q' key to abort this operation."
char = INKEY(5)
IF char = 81 .OR. char = 113
aborted = .T.
ELSE
USE jobs
ENDIF &&(char = 81 .OR. char = 113)
ENDDO &&(WHILE NETERR() .AND. .NOT. aborted)
IF .NOT. aborted
*
* Set up database index file (created with INDEX jobs ON shopord TO jobnum)
SET INDEX TO jobnum
DO WHILE continue
*
* Start of critical section
GO BOTTOM
mshopord = shopord + 1
APPEND BLANK
DO WHILE NETERR()
@ 23,0 CLEAR
@ 23,0 SAY "Append failed. Please wait."
APPEND BLANK
ENDDO
@ 23,0 CLEAR
REPLACE shopord WITH mshopord
*
* End of critical section
*
* Further data entry routines go here.
@ 23,0 CLEAR
@ 23,0 SAY "Do you wish to add another job? "GET continue PICT "L"
READ
ENDDO &&(WHILE continue)
*
* Close data file and index
USE
ENDIF
RETURN
In the above program segment I have used the available locking calls to
create what appears to be a correct program. Whenever I used a command that
is affected by the network environment, I have checked for errors and made
sure that all my locks are properly set. However, it is still possible for
two operators working simultaneously to "hit" the section at the beginning of
the "DO WHILE continue" loop at the same time, thus attempting to enter two
different jobs with the same job number.
Now, I know that I could re-write the section in question to make sure that
the job number I am going to replace into the record is unique, but the code
would be sizable and not at all intuitive to the person who may maintain it
later. A better way to solve the problem is to somehow make sure that no
workstation attempts to enter into the critical section of code at the same
time someone else on the network is executing it.
There are several ways of accomplishing this. I didn't want to use a
one-record database, as I already am trying to work out ways to save file
handles. I didn't want to create some sort of signalling within Clipper, as
there is no guarantee that all my programs will be in that language. I also
wanted to minimize code size, and make the use of the functions simple. I
really did want to use the Netware semaphore function calls to coordinate this
operation. That way I would use a resource that is not in control of the
workstation, and I could use the semaphore arbitration that is built into
Netware (if more than one station is waiting on the semaphore, and whoever is
holding it releases the semaphore, the station that has been waiting longest
is signalled to proceed).
Thus was born CRITICAL.ASM, with it's five functions:
INITCRIT(<expC>) - initialize the critical procedure lock system
expC is the "name" that the semaphore will use, and
should be unique for each critical section in your
program. If the call completes successfully, it returns
numeric 0. If not, it returns an error code between
1 and 255
1 - bad number of parameters
2 - bad parameter type
150 - file server working memory overflow
(too many simultaneous semaphores)
254 - invalid inital value (should NEVER occur)
255 - invalid string length (must be between
1 and 127 characters long)
STCRIT() - check to see if we can start the critical section.
If it is OK to continue, it returns numeric 0. If not,
it also returns an error code between 1 and 255
254 - timeout (currently set at 10 sec., see code)
255 - invalid semaphore handle (should NEVER occur)
ENDCRIT() - signal the station waiting the longest that it can
proceed. If it works, returns numeric 0. If not,
it returns an error between 1 and 255
1 - semaphore overflow (should NEVER occur)
255 - invalid semaphore handle (should NEVER occur)
CLOSCRIT() - close out critical procedure section locking system
If it works, returns 0. If not, returns 255 to indicate
invalid semaphore handle, which should never occur.
CRITNUMS() - return the number of stations with the critical lock
of the same name. This should be called only after
the INITCRIT() call and before a CLOSCRIT() call. The
number reported is the number of stations AT THE TIME
OF THE INITCRIT() CALL. If others initialize after you
do, they won't be counted.
The way you use the functions is demonstrated in the re-written code section
below:
PROCEDURE addjob
*
* Declare the private variables (mshopord is where we hold the job number)
PRIVATE aborted,continue,result,mshopord
EXTERNAL INITCRIT,STCRIT,ENDCRIT,CLOSCRIT
SET EXCLUSIVE OFF
aborted = .F.
continue = .T.
result = INITCRIT("addjob")
IF result = 0
*
* Set up database file
USE jobs
DO WHILE NETERR() .AND. .NOT. aborted
@ 23,0 CLEAR
@ 23,0 SAY "File is currently locked. Wait 5 seconds or press spacebar to retry."
@ 24,0 SAY "Or, press the 'Q' key to abort this operation."
char = INKEY(5)
IF char = 81 .OR. char = 113
aborted = .T.
ELSE
USE jobs
ENDIF &&(char = 81 .OR. char = 113)
ENDDO &&(WHILE NETERR() .AND. .NOT. aborted)
IF .NOT. aborted
*
* Set up database index file (created with INDEX jobs ON shopord TO jobnum)
SET INDEX TO jobnum
DO WHILE continue
result = STCRIT()
IF result = 0
*
* Start of critical section
GO BOTTOM
mshopord = shopord + 1
APPEND BLANK
REPLACE shopord WITH mshopord
*
* End of critical section
result = ENDCRIT()
*
* Further data entry routines go here.
@ 23,0 CLEAR
@ 23,0 SAY "Do you wish to add another job? "GET continue PICT "L"
READ
ENDIF &&(result = 0)
ENDDO &&(WHILE continue)
*
* Close data file and index
USE
*
* Close semaphore
result = CLOSCRIT()
ENDIF &&(.NOT. aborted)
ENDIF &&(result = 0)
RETURN
In the above program, the critical procedure module is initialized with the
name "addjob". This is hardcoded into the program, so that all workstations
that are running this code will be using the same semaphore. At the point
where the critical section is, we first call STCRIT() to see if we can
proceed. This procedure calls the fileserver to examine the semaphore, and
if it is not available it waits (up to 10 sec.) until it is available. If
the semaphore is made available, STCRIT returns 0 and we perform the critical
section. Note that in that section I no longer check to see if my APPEND
BLANK causes a network error -- the critical section arbitration makes sure
that only one workstation attempts the APPEND at one time, and we wouldn't be
executing this code if the file was in exclusive use somewhere else. After
the critical code has been executed, we release the semaphore with the
ENDCRIT() call, and if anyone else is waiting, they are told to proceed by
the fileserver.
In the files that are in this archive are the source code for the UDF's (I
admit that I developed them with MASM 3.00, as I have no later version. This
means that the code was developed with EXTENDA.MAC rather than EXTENDA.INC
which requires MASM 5.00. I also added three extra macros to handle the new
parameter calls and the new return call, these are in EXTENDA2.MAC), the
linkable .OBJ file, a short test program called CRITTEST.PRG, and the linking
command file CRITTEST.LNK (compatible with Microsoft Link or what I use, Turbo
Link from Borland [part of Turbo C]). The correct commands for PLINK86 are:
FI crittest, critical
LIB clipper;
If you have questions (welcomed) or comments (craved!), please send Email
here on Exec BBS (for those that get this through the "bbs grapevine", Exec
is a multi-gigabyte multi-line granddaddy of IBM bulletin boards, and as of
this writing it has over 60,000 files to download. The number is (414)
964-5160. It is available on PC-Pursuit, but since it is a local call for me
I don't know how to use that to access it! :-) ) This is just the first
thing I've done that I'm really proud of, but I have lots of other stuff for
specific situations that may be useful to you just for the asking.
Thanks for looking at this,
Scott Chamberlain
1505 N. Franklin Place, #605
Milwaukee, WI 53202
(If you want to, call me at work at (414) 445-6800 between 9AM and 5PM
Central time. Just ask for me.)