« Tech Support: Using Xwire | Main | Tech Support: Naming conventions »

Tech Support: Allocating shared resources

Kim wrote:

Hi David

I have 5 semaphores declared as
sVeryImportant     defsem   1
sQuiteImportant    defsem   2
sImportant         defsem   3
sLessImportant     defsem   4
sNotImportant      defsem   5
The above semaphores decided which task controls a motor. Obviously only one task can control the motor at any one time.

However, the above semaphores are prioritised and the Very Important semaphore cannot give up the task until its finished its task. And likewise a less important semaphores cannot take over from a more important semaphore, though a more important semaphore MUST take over from a less important semaphore.
Hi Kim,

I think you are confusing semaphores with something else, possibly enumerated variables. In "SPLatSPeak" a semaphore is a 1-bit variable. It can only have one of two values: True or False.

The challenge you raise is that of resource sharing, in your case prioritized resource sharing, meaning a higher priority task can preempt a lower priority one, but not visa versa. (I am here meaning a task under MultiTrack.)

The way I usually handle this kind of situation is with what I call a token. I imagine a token to be a little metal disc. There is only one in the whole system, and whoever holds it has access to the resource. I write a "token manager" to control the issuing of the token according to set rules (in your case a simple priority scheme).

Each task must be well-behaved and play by the rules. In this case the rules are:
  1. Only one task may hold the token.
  2. A task bids for the token using its priority level.
  3. If the task is granted the token it may use the shared resource only until the next yielding instruction.
  4. After a yield the task must re-validate the token before again accessing the shared resource. If the token manager refuses to re-validate the token, it means someone with a higher priority has grabbed it.
  5. When the task has finished its use of the token, it must hand it back so a task of lower priority can use it. It must do this after winning or re-validating the token and before yielding.
  6. Every task must have a unique priority number.
Rule 4 is very important, because it is the key to ensuring that a holder of the token can discover that someone else has outbid it. This will stop the task from trying to use the resource that someone else now owns. It will also let the task act upon the "lost the token" event with some appropriate action.

Rule 6 makes the programming ever so much simpler and safer than if you allow multiple tasks to share a priority level.

The following code illustrates the idea. It's a bit longer than I like examples to become, because I have included 3 tasks competing for one resource. The program uses 4 front panel LEDs on an MS120 to display one of 3 light patterns. Which pattern is displayed depends on the 3 push buttons under the display plus their relative priorities. The righthand button has the highest priority, the lefthand button has the lowest.

This is also a useful example of passing values into and out of subroutines in X. The token manager has 3 subroutines that can be called, TKN_Bid, TKN_Validate and TKN_Release.
        LaunchTask      RotRight
        LaunchTask      RotLeft
        LaunchTask      Count
        RunTasksForever

;===== Token manager ==================

bTKN_CurrentLevel       defBYTE

;Bid for the token with a priority level in X.
;Returns True if the token is granted

;The bid function simply compares the new bid with
;the existing value and decides accordingly
TKN_Bid:
        Recall          bTKN_CurrentLevel
        CompareR
        BranchR
        Target          TKN_Deny        ;X=Y
        Target          TKN_Deny        ;X .GT. Y
        Target          TKN_Grant       ;X  .LT. Y

;The re-validate function also compares the new and the old,
;but with a slightly different rule. 
TKN_Validate:
        Recall          bTKN_CurrentLevel
        CompareR
        BranchR
        Target          TKN_ValGrant    
        Target          TKN_ValDeny    
        Target          TKN_ValError 

TKN_Release:
        SetMem          bTKN_CurrentLevel,0
        Return

TKN_Deny:
TKN_ValDeny:
        LoadX           F
        Return

TKN_Grant:
        Swap
        Store           bTKN_CurrentLevel
TKN_ValGrant    
        LoadX           T
        Return

;Here someone tried validating illegally, because the
;"new" bid is greater than the existing token holder's bid.
;I opt to crash the machine so I can detect such a coding mistake.
TKN_ValError:
        GoSub           TKN_ValError

;===== Rotate right task ======
bRR     defBYTE
RotRight:
        SetMem          bRR,'11
RR_0:
        WaitOn          iRR
RR_1:
        YieldTask
        LoadX           RR_Priority     ;My priority
        GoSub           TKN_Bid         ;Ask for token
        GoIfF           RR_0            ;G/ denied
RR_Set2:
        Recall          bRR             ;Get pattern ...
        Push
        LoadX           'FF
        OutputM         oLEDs           ;... and display
        RorM                            ;Change pattern
        Store           bRR
RR_2:
        Pause           50
        GoIfInOn        iRR,RR_2a       ;Still required?
        GoSub           TKN_Release     ;No, give back token
        GoTo            RR_0

RR_2a:
        LoadX           RR_Priority
        GoSub           TKN_Validate    ;Check I still own token
        GoIfF           RR_0            ;Lost it!!!!
        GoTo            RR_Set2 

;===== Rotate left task ======
bRL     defBYTE
RotLeft:
        SetMem          bRL,'11
RL_0:
        WaitOn          iRL
RL_1:
        YieldTask
        LoadX           RL_Priority
        GoSub           TKN_Bid
        GoIfF           RL_0
RL_Set2:
        Recall          bRL
        Push
        LoadX           'FF
        OutputM         oLEDs
        RolM
        Store           bRL
RL_2:
        Pause           10
        GoIfInOn        iRL,RL_2a
        GoSub           TKN_Release
        GoTo            RL_0

RL_2a:
        LoadX           RL_Priority
        GoSub           TKN_Validate
        GoIfF           RotLeft
        GoTo            RL_Set2 


;===== Count task ======
bCC     defBYTE
Count:
        SetMem          bCC,'10
CC_0:
        WaitOn          iCC
CC_1:
        YieldTask
        LoadX           CC_Priority
        GoSub           TKN_Bid
        GoIfF           CC_0
CC_Set2:
        Recall          bCC
        Push
        LoadX           'FF
        OutputM         oLEDs
        IncX
        Store           bCC
CC_2:
        Pause           5
        GoIfInOn        iCC,CC_2a
        GoSub           TKN_Release
        GoTo            CC_0

CC_2a:
        LoadX           CC_Priority
        GoSub           TKN_Validate
        GoIfF           CC_0
        GoTo            CC_Set2 

RR_Priority     EQU     5
RL_Priority     EQU     10
CC_Priority     EQU     15

iRR     iEQU    14
iRL     iEQU    15
iCC     iEQU    16

oLEDS   oEQU    8
There are many possible variations of the token theme. For example, a power supply may have a limited capacity to drive motors, so only 3 out of 10 motors may be on at once. Or the power supply may be capably of starting only one motor at once, which could lead to a token that expires after a couple of seconds. You could even have motors with different current draws, and use an "analog" token to ensure that the total current drawn from the power supply never exceeded its capacity.

TrackBack

TrackBack URL for this entry:
http://www.splatco.com/cgi-sys/cgiwrap/microcon/managed-mt/mt-tb.cgi/69

Post a comment

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)


About

This page contains a single entry from the blog posted on August 19, 2007 4:51 PM.

The previous post in this blog was Tech Support: Using Xwire.

The next post in this blog is Tech Support: Naming conventions.

Many more can be found on the main index page or by looking through the archives.

Creative Commons License
This weblog is licensed under a Creative Commons License.
Powered by
Movable Type 3.33