Kim wrote:
Hi David
I have 5 semaphores declared assVeryImportant defsem 1 sQuiteImportant defsem 2 sImportant defsem 3 sLessImportant defsem 4 sNotImportant defsem 5The 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:
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.
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:
- Only one task may hold the token.
- A task bids for the token using its priority level.
- If the task is granted the token it may use the shared resource only until the next yielding instruction.
- 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.
- 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.
- Every task must have a unique priority number.
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.
