home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / lan / critical / read.me < prev   
Text File  |  1988-07-15  |  10KB  |  246 lines

  1. This is a set of routines to provide a method of synchronizaton between
  2. multiple workstations running Clipper programs simultaneously (for programs
  3. compiled with the Summer '87 version).
  4.  
  5. I developed these routines to allow easy creation of data entry routines
  6. that will handle multiple operators working on the same data file.  For
  7. example, lets say we are writing a program that will be used to log orders
  8. into a production order file.  Each production order has a unique number to
  9. help track it through the plant.
  10.  
  11. The problem comes when two operators choose the "Add a Job" section at the
  12. same time.  I don't want to set it up so one operator is in the file
  13. exclusively, because the purpose is to allow as many operators as possible.
  14. But, I want to make sure that only one person at a time is getting the next
  15. available number, incrementing it, and saving it back to the data file.
  16.  
  17. An example of code that contains the possibility of error is as follows
  18. (This example assumes that we are running a multi-user situation, note the
  19. use of already existing CLIPPER syntax to support network operations):
  20.  
  21. PROCEDURE addjob
  22. *
  23. * Declare the private variables (mshopord is where we hold the job number)
  24. PRIVATE aborted,continue,mshopord
  25.  
  26. SET EXCLUSIVE OFF
  27. aborted = .F.
  28. continue = .T.
  29. *
  30. * Set up database file
  31. USE jobs
  32. DO WHILE NETERR() .AND. .NOT. aborted
  33.    @ 23,0 CLEAR
  34.    @ 23,0 SAY "File is currently locked.  Wait 5 seconds or press spacebar to retry."
  35.    @ 24,0 SAY "Or, press the 'Q' key to abort this operation."
  36.    char = INKEY(5)
  37.    IF char = 81 .OR. char = 113
  38.       aborted = .T.
  39.    ELSE
  40.       USE jobs
  41.    ENDIF &&(char = 81 .OR. char = 113)
  42. ENDDO &&(WHILE NETERR() .AND. .NOT. aborted)
  43. IF .NOT. aborted
  44.    *
  45.    * Set up database index file (created with INDEX jobs ON shopord TO jobnum)
  46.    SET INDEX TO jobnum
  47.    DO WHILE continue
  48.       *
  49.       * Start of critical section
  50.       GO BOTTOM
  51.       mshopord = shopord + 1
  52.       APPEND BLANK
  53.       DO WHILE NETERR()
  54.      @ 23,0 CLEAR
  55.      @ 23,0 SAY "Append failed.  Please wait."
  56.      APPEND BLANK
  57.       ENDDO
  58.       @ 23,0 CLEAR
  59.       REPLACE shopord WITH mshopord
  60.       *
  61.       * End of critical section
  62.       *
  63.       * Further data entry routines go here.
  64.       @ 23,0 CLEAR
  65.       @ 23,0 SAY "Do you wish to add another job? "GET continue PICT "L"
  66.       READ
  67.    ENDDO &&(WHILE continue)
  68.    *
  69.    * Close data file and index
  70.    USE
  71. ENDIF
  72. RETURN
  73.  
  74. In the above program segment  I have used the available locking calls to
  75. create what appears to be a correct program.  Whenever I used a command that
  76. is affected by the network environment, I have checked for errors and made
  77. sure that all my locks are properly set.  However, it is still possible for
  78. two operators working simultaneously to "hit" the section at the beginning of
  79. the "DO WHILE continue" loop at the same time, thus attempting to enter two
  80. different jobs with the same job number.
  81.  
  82. Now, I know that I could re-write the section in question to make sure that
  83. the job number I am going to replace into the record is unique, but the code
  84. would be sizable and not at all intuitive to the person who may maintain it
  85. later.  A better way to solve the problem is to somehow make sure that no
  86. workstation attempts to enter into the critical section of code at the same
  87. time someone else on the network is executing it.
  88.  
  89. There are several ways of accomplishing this.  I didn't want to use a
  90. one-record database, as I already am trying to work out ways to save file
  91. handles.  I didn't want to create some sort of signalling within Clipper, as
  92. there is no guarantee that all my programs will be in that language.  I also
  93. wanted to minimize code size, and make the use of the functions simple.  I
  94. really did want to use the Netware semaphore function calls to coordinate this
  95. operation.  That way I would use a resource that is not in control of the
  96. workstation, and I could use the semaphore arbitration that is built into
  97. Netware (if more than one station is waiting on the semaphore, and whoever is
  98. holding it releases the semaphore, the station that has been waiting longest
  99. is signalled to proceed).
  100.  
  101. Thus was born CRITICAL.ASM, with it's five functions:
  102.    INITCRIT(<expC>)  -  initialize the critical procedure lock system
  103.             expC is the "name" that the semaphore will use, and
  104.             should be unique for each critical section in your
  105.             program.  If the call completes successfully, it returns
  106.             numeric 0.  If not, it returns an error code between
  107.             1 and 255
  108.                   1   - bad number of parameters
  109.                   2   - bad parameter type
  110.                   150 - file server working memory overflow
  111.                     (too many simultaneous semaphores)
  112.                   254 - invalid inital value (should NEVER occur)
  113.                   255 - invalid string length (must be between
  114.                     1 and 127 characters long)
  115.  
  116.    STCRIT()          -  check to see if we can start the critical section.
  117.             If it is OK to continue, it returns numeric 0.  If not,
  118.             it also returns an error code between 1 and 255
  119.                   254 - timeout (currently set at 10 sec., see code)
  120.                   255 - invalid semaphore handle (should NEVER occur)
  121.  
  122.    ENDCRIT()         -  signal the station waiting the longest that it can
  123.             proceed.  If it works, returns numeric 0.  If not,
  124.             it returns an error between 1 and 255
  125.                   1   - semaphore overflow (should NEVER occur)
  126.                   255 - invalid semaphore handle (should NEVER occur)
  127.  
  128.    CLOSCRIT()        -  close out critical procedure section locking system
  129.             If it works, returns 0.  If not, returns 255 to indicate
  130.             invalid semaphore handle, which should never occur.
  131.  
  132.    CRITNUMS()        -  return the number of stations with the critical lock
  133.             of the same name.  This should be called only after
  134.             the INITCRIT() call and before a CLOSCRIT() call.  The
  135.             number reported is the number of stations AT THE TIME
  136.             OF THE INITCRIT() CALL.  If others initialize after you
  137.             do, they won't be counted.
  138.  
  139.  
  140. The way you use the functions is demonstrated in the re-written code section
  141. below:
  142.  
  143. PROCEDURE addjob
  144. *
  145. * Declare the private variables (mshopord is where we hold the job number)
  146. PRIVATE aborted,continue,result,mshopord
  147. EXTERNAL INITCRIT,STCRIT,ENDCRIT,CLOSCRIT
  148.  
  149. SET EXCLUSIVE OFF
  150. aborted = .F.
  151. continue = .T.
  152.  
  153. result = INITCRIT("addjob")
  154. IF result = 0
  155.    *
  156.    * Set up database file
  157.    USE jobs
  158.    DO WHILE NETERR() .AND. .NOT. aborted
  159.       @ 23,0 CLEAR
  160.       @ 23,0 SAY "File is currently locked.  Wait 5 seconds or press spacebar to retry."
  161.       @ 24,0 SAY "Or, press the 'Q' key to abort this operation."
  162.       char = INKEY(5)
  163.       IF char = 81 .OR. char = 113
  164.      aborted = .T.
  165.       ELSE
  166.      USE jobs
  167.       ENDIF &&(char = 81 .OR. char = 113)
  168.    ENDDO &&(WHILE NETERR() .AND. .NOT. aborted)
  169.    IF .NOT. aborted
  170.       *
  171.       * Set up database index file (created with INDEX jobs ON shopord TO jobnum)
  172.       SET INDEX TO jobnum
  173.       DO WHILE continue
  174.      result = STCRIT()
  175.      IF result = 0
  176.         *
  177.         * Start of critical section
  178.         GO BOTTOM
  179.         mshopord = shopord + 1
  180.         APPEND BLANK
  181.         REPLACE shopord WITH mshopord
  182.         *
  183.         * End of critical section
  184.         result = ENDCRIT()
  185.         *
  186.         * Further data entry routines go here.
  187.         @ 23,0 CLEAR
  188.         @ 23,0 SAY "Do you wish to add another job? "GET continue PICT "L"
  189.         READ
  190.      ENDIF &&(result = 0)
  191.       ENDDO &&(WHILE continue)
  192.       *
  193.       * Close data file and index
  194.       USE
  195.       *
  196.       * Close semaphore
  197.       result = CLOSCRIT()
  198.    ENDIF &&(.NOT. aborted)
  199. ENDIF &&(result = 0)
  200. RETURN
  201.  
  202. In the above program, the critical procedure module is initialized with the
  203. name "addjob".  This is hardcoded into the program, so that all workstations
  204. that are running this code will be using the same semaphore.  At the point
  205. where the critical section is, we first call STCRIT() to see if we can
  206. proceed.  This procedure calls the fileserver to examine the semaphore, and
  207. if it is not available it waits (up to 10 sec.) until it is available.  If
  208. the semaphore is made available, STCRIT returns 0 and we perform the critical
  209. section.  Note that in that section I no longer check to see if my APPEND
  210. BLANK causes a network error -- the critical section arbitration makes sure
  211. that only one workstation attempts the APPEND at one time, and we wouldn't be
  212. executing this code if the file was in exclusive use somewhere else.  After
  213. the critical code has been executed, we release the semaphore with the
  214. ENDCRIT() call, and if anyone else is waiting, they are told to proceed by
  215. the fileserver.
  216.  
  217. In the files that are in this archive are the source code for the UDF's (I
  218. admit that I developed them with MASM 3.00, as I have no later version.  This
  219. means that the code was developed with EXTENDA.MAC rather than EXTENDA.INC
  220. which requires MASM 5.00.  I also added three extra macros to handle the new
  221. parameter calls and the new return call, these are in EXTENDA2.MAC), the
  222. linkable .OBJ file, a short test program called CRITTEST.PRG, and the linking
  223. command file CRITTEST.LNK (compatible with Microsoft Link or what I use, Turbo
  224. Link from Borland [part of Turbo C]).  The correct commands for PLINK86 are:
  225.  
  226. FI crittest, critical
  227. LIB clipper;
  228.  
  229. If you have questions (welcomed) or comments (craved!), please send Email
  230. here on Exec BBS (for those that get this through the "bbs grapevine", Exec
  231. is a multi-gigabyte multi-line granddaddy of IBM bulletin boards, and as of
  232. this writing it has over 60,000 files to download.  The number is (414)
  233. 964-5160.  It is available on PC-Pursuit, but since it is a local call for me
  234. I don't know how to use that to access it! :-) )  This is just the first
  235. thing I've done that I'm really proud of, but I have lots of other stuff for
  236. specific situations that may be useful to you just for the asking.
  237.  
  238. Thanks for looking at this,
  239.  
  240. Scott Chamberlain
  241. 1505 N. Franklin Place, #605
  242. Milwaukee, WI 53202
  243.  
  244. (If you want to, call me at work at (414) 445-6800 between 9AM and 5PM
  245. Central time.  Just ask for me.)
  246.