|
||
RESOURCE LOCKSOVERVIEWSee the section COMMON RESOURCES, page 181, for an introduction to the problem of shared variables, ports and external hardware in any multitasking system. The file #ROBIN.TDS provides a method of allocating a resource to one task at a time, locking out any other tasks which request the resource concurrently. The rules for adding lock bytes are:
q Put <lock byte name> LOCK before any part of the program where the task needs resource access. q Put <lock byte name> UNLOCK after the task has finished with the resource. q Zero all lock bytes at power-up initialisation (or use UNLOCK ).
EXAMPLE OF RESOURCE LOCKSThe file #SHARE.TDS contains an example of how a clash of resource requests can result in problems, and also shows how this is corrected with the words LOCK and UNLOCK . To use this demonstration first compile the file #TASKTRY.TDS. Next compile #SHARE.TDS and then type CLASH return to see how tasks FRED and HARRY both access the serial output and variable MEMORY , giving serious disruptive interruption. HARRY should always show numbers 11 to 20 but some of the tries give faults due to incorrect resource sharing. This is a typical output:
Fred 1 2 3 4 5 6 7 8 9 10 Fred 1 2 3 4 5 6 7 8 9 10 Harry 11 12 13 14 15 16 17 18 19 20 Fred fault Fred 1 2 3 4 5 6 7 8 9 10 Fred 1 2 3 4 5 6 7 8 9 10 Fred 1 2 3 4 5 6 7 8 9 10 Harry 11 12 13 14 15 16 17 18 19 20 Fred 1 2 3 4 5 6 7 8 9 10 Fred 1 2 3 4 5 6 7 8 9 10 Fred 1 2 3 4 5 6 7 8 9 10 Fred 1 2 3 4 5 6 7 8 9 10 Fred 1 2 3 4 5 6 Harry 1112 1314 1516 1718 19 20 21 22 23 24 fault Fred 1 2 3 4 5 6 7 8 9 10
While watching the output type HARRY HALT FRED HALT return to stop the two background tasks, leaving only the OPERATOR task executing Forth. While you do this the input will be broken up by HARRY and FRED but the OPERATOR task will accept the input and arrest the other two. Next type NOCLASH return and watch how tasks FRED and HARRY work together sharing both serial output and the variable MEMORY in a co-operative manner. FRED shows the numbers 21 to 30 and HARRY displays 31 to 40 without either being broken up by the other. This is how it will look:
Fred 21 22 23 24 25 26 27 28 29 30 Fred 21 22 23 24 25 26 27 28 29 30 Harry 31 32 33 34 35 36 37 38 39 40 Fred 21 22 23 24 25 26 27 28 29 30 Fred 21 22 23 24 25 26 27 28 29 30 Fred 21 22 23 24 25 26 27 28 29 30 Fred 21 22 23 24 25 26 27 28 29 30 Harry 31 32 33 34 35 36 37 38 39 40 Fred 21 22 23 24 25 26 27 28 29 30 Fred 21 22 23 24 25 26 27 28 29 30 Fred 21 22 23 24 25 26 27 28 29 30 Fred 21 22 23 24 25 26 27 28 29 30 Fred 21 22 23 24 25 26 27 28 29 30 Harry 31 32 33 34 35 36 37 38 39 40 Fred 21 22 23 24 25 26 27 28 29 30
You will need to power down to stop the demonstration since the third operational task, the OPERATOR , is not properly locked in this program and you may not be able to stop the other tasks from the OPERATOR task-another example of how you need to look out for every shared resource. The code from #SHARE.TDS for the latter (correct) demonstration is reproduced here. The faulty one is very similar but does not have the lines with LOCK and UNLOCK .
DECIMAL VARIABLE MEMORY \ variable used by more than one task VARIABLE #MEMORY \ lock byte for variable MEMORY #MEMORY 1+ CONSTANT #I/O \ lock byte for serial output
: 21-30 \ A background task giving its name and \ numbers 21 to 30 FRED ACTIVATE 500 MS \ allow time for OPERATOR task to \ finish serial output BEGIN #I/O LOCK \ grab serial output for FRED CR ." FRED " 10 SPACES #MEMORY LOCK \ grab variable MEMORY for FRED 20 MEMORY ! 10 0 DO 1 MEMORY +! MEMORY @ . PAUSE LOOP #MEMORY UNLOCK \ release variable MEMORY for \ use by other tasks #I/O UNLOCK \ release serial output for \ use by other tasks 500 MS AGAIN ;
: 31-40 \ A background task giving its name and \ numbers 31 to 40 HARRY ACTIVATE 500 MS \ allow time for OPERATOR task to \ finish serial output BEGIN #I/O LOCK \ grab serial output for HARRY CR ." HARRY " #MEMORY LOCK \ grab variable MEMORY for HARRY 30 MEMORY ! 10 0 DO 1 MEMORY +! MEMORY @ . PAUSE LOOP #MEMORY UNLOCK \ release variable MEMORY for \ use by other tasks #I/O UNLOCK \ release serial output for \ use by other tasks 2200 MS AGAIN ;
: NOCLASH \ Demonstration of proper behaviour \ with correctly shared resources #MEMORY UNLOCK #I/O UNLOCK TASKS 21-30 31-40 START ;
HOW LOCK BYTES WORKThe protection method allocates one byte for every shared item, whether a variable, block of memory, input/output pin or external device like a Liquid Crystal Display. It is up to you, the programmer, to decide what needs protecting and where in your program the resource needs to be dedicated to one task. A lock byte can be an ordinary variable but that is a waste of 8 of the 16 bits defined. The example above shows how one variable can provide two lock bytes. The system works because of a special 'test-and-set' instruction in the H8/532, something common in the minicomputer world but rare in microprocessors. It is an indivisible machine code instruction inside LOCK which tests bit 7 of the lock byte and sets the N flag if it found logic 1, clearing the N flag otherwise. The instruction then sets the bit 7. LOCK will exit with the bit 7 set if it was originally zero, i.e. the resource is free. Otherwise program execution is held up in LOCK until another task executes UNLOCK on the same lock byte to free the resource. While waiting the task calls PAUSE (in fact by machine code software interrupt for speed) so that other tasks get an opportunity to proceed. Because LOCK depends on the indivisible TAS instruction this sharing mechanism can be used by the interrupt driven multitasking built into the Forth ROM of the TDS2020F as well as the Pre-emptive or Co-operative styles. The file #ELOCK.TDS defines equivalent functions ELOCK and EUNLOCK for use with variables in extended memory.
|