Calling ByRef in Task's

Discussion about the ZBasic language including the System Library. If you're not sure where to post your message, do it here. However, do not make test posts here; that's the purpose of the Sandbox.
Post Reply
hakha4
Posts: 47
Joined: 01 June 2009, 21:23 PM
Location: Moholm Sweden

Calling ByRef in Task's

Post by hakha4 »

Hi!
Trying to control relay's with Netmedia LCD+. ZBasic doc says it's not recommended to pass value to task's ByRef. Code below works if I pass a value ByREf in an ordinary Sub but not in a task. Tried to preserve the RelayStatus value in a tmpRelayStatus variable but still not working. Could anyone give me a hint how to get Byref to work in a task. Tried to solve the problem by reading the doc's/forum but I'm stucked. Any help would be appreciated
Regards Hucke

[/code]
test.bas
'***************************''''
'
'
'
'
'
Sub Main

RelaysALL_OFF' set relay's off and initialize 'tmpRelayStatus' to &H00

Do

'#### DON't WORK
CallTask SwitchRelay(tmpRelayStatus,3,1),RelayStack
CallTask SwitchRelay(tmpRelayStatus,4,1),RelayStack


'#### WORKING
call SwitchRelay(tmpRelayStatus,2,1)
call SwitchRelay(tmpRelayStatus,1,1)

Loop

End Sub


' Relay_1.Bas
'
' Public Sub Relays_OFF()- turn OFF ALL relays
' Sub SwitchRelay(ByRef RelayStatus as Byte, ByVal RelayNumber as Byte)
'
'Call RelaysALL_OFF() in Sub Main to set all relay's off as default and to set 'tmpRelayStatus'

Public RelayMsg(0 to 7) As String

Public RelayStack(1 To 73) As Byte
Public tmpRelayStatus As Byte 'to hold value of RelayStatus
'********************************************************************************************

Sub SwitchRelay(Byref RelayStat as Byte,ByVal RelayNumber as Byte,ByVal tmpStatus As Byte)
' Turns ON/OFF specified relay 1-8
If RelayNumber > 8 Then
Exit Sub
End If

Select Case tmpStatus
Case 1'On
Call PutBit(RelayStat, RelayNumber-1, 1)
Case 0'Off
Call PutBit(RelayStat, RelayNumber-1, 0)
End Select

Call PutByte_3(&H12)
Call PutByte_3(RelayStat XOR &Hff)
Relay_Msg(RelayStat)
Call Delay(0.0)
End Sub


Public Sub RelaysALL_OFF()
Dim RelayStatus As Byte

RelayStatus = &H00
Call PutByte_3(&H12) ' relay command
Call PutB_3(RelayStatus XOR &Hff) ' turn OFF all relay's
Call Relay_Msg(RelayStatus)

tmpRelayStatus = RelayStatus
End Sub

Public Sub Relay_Msg(ByRef RelayStatus As Byte)
Dim X As Integer
Dim Y As Integer
Dim TmpBitStatus As Byte
Dim tmpMsg As String
Dim tmpPos As Byte

RelayMsg(0) = "Pump = "
RelayMsg(1) = "Chlor = "
RelayMsg(2) = "VP = "
RelayMsg(3) = "Sun = "
RelayMsg(4) = "Light1 ="
RelayMsg(5) = "Light2 ="
RelayMsg(6) = "Light3 ="
RelayMsg(7) = "Light4 ="

tmpPos = 0

For X = 0 To 7
TmpBitStatus = GetBit(RelayStatus, X)

tmpMsg = RelayMsg(X) & Cstr(TmpBitStatus)

If tmpPos > 79 Then
tmpPos = 0
End If

LCDSetCursorPosition(tmpPos)
PutStr_3 tmpMsg
tmpPos = tmpPos + 10

Next
End Sub
GTBecker
Posts: 616
Joined: 17 January 2006, 19:59 PM
Location: Cape Coral

Post by GTBecker »

This worthless, unrelated post has been deleted.
Last edited by GTBecker on 22 September 2009, 19:18 PM, edited 1 time in total.
Tom
mikep
Posts: 796
Joined: 24 September 2005, 15:54 PM

Post by mikep »

I think Tom misunderstood your question. Here is a working test program that passes parameters by reference to a task.

Code: Select all

Dim taskStack(1 to 100) as Byte

Public Sub Main()
	Dim tmpRelayStatus as Byte
	tmpRelayStatus = &H00
	Debug.Print "Outside task "; tmpRelayStatus
	CallTask RelayTask(tmpRelayStatus), taskStack
	Call Sleep(1.0)
	Debug.Print "Outside task "; tmpRelayStatus
	Do ' loop forever
	Loop
End Sub

Public Sub RelayTask(ByRef relayStatus as Byte)
	Debug.Print "Inside task "; relayStatus
	relayStatus = &H55
	Debug.Print "Inside task "; relayStatus
	Do ' loop forever
	Loop 
End Sub
The compiler issues the warning as documented in CallTask about passing parameters by reference (ByRef) and the code produces the expected output of

Code: Select all

Outside task 0
Inside task 0
Inside task 85
Outside task 85
I am not sure exactly what the problem is that you are having. Can you describe a little more what is failing and where? Reading the code it looks like you could simply use a module level variable. Here is the same example as above rewritten with a module level variable.

Code: Select all

Dim relayStatus as Byte
Dim taskStack(1 to 100) as Byte

Public Sub Main()	
	relayStatus = &H00
	Debug.Print "Outside task "; relayStatus
	CallTask RelayTask(), taskStack
	Call Sleep(1.0)
	Debug.Print "Outside task "; relayStatus
	Do ' loop forever
	Loop
End Sub

Public Sub RelayTask()
	Debug.Print "Inside task "; relayStatus
	relayStatus = &H55
	Debug.Print "Inside task "; relayStatus
	Do ' loop forever
	Loop 
End Sub
Mike Perks
GTBecker
Posts: 616
Joined: 17 January 2006, 19:59 PM
Location: Cape Coral

Post by GTBecker »

> ... Tom misunderstood your question...

Quite right. I read LCD-X; sorry.
Tom
hakha4
Posts: 47
Joined: 01 June 2009, 21:23 PM
Location: Moholm Sweden

Post by hakha4 »

Thank's Mike for reply. I've tried Your approach earlier but missed to put 'Sleep' between calls!. Sleep (1.0) solved the problem.
Regards Hucke
hakha4
Posts: 47
Joined: 01 June 2009, 21:23 PM
Location: Moholm Sweden

Post by hakha4 »

By the way. Are there any thumb roles for when to use delay/pause in Task's .I thougt one of the advantages with task's was that the procedure finish before next call is launched.
spamiam
Posts: 739
Joined: 13 November 2005, 6:39 AM

Post by spamiam »

hakha4 wrote:By the way. Are there any thumb roles for when to use delay/pause in Task's .I thougt one of the advantages with task's was that the procedure finish before next call is launched.
For me, the only rule of thumb is the more general case very issue that the compiler warned about when using ByRef variable passing. That issue is sharing of resources between processes, RAM being one of them.

You have to be very careful about how you share all this stuff because another process may become active when the other proces was in the middle of doing something and got suspended by a task switch. If you assume that a certain process runs to completion without interruption (i.e. it is "atomic"), then sometimes you will have some very odd and hard-to-figure-out problems.

By passing values "ByVal", the task has its own variable, and is not dependent on other tasks behaving in any specific manner. Go ahead and pass by reference, but be careful!

-Tony
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

hakha4 wrote:I thougt one of the advantages with task's was that the procedure finish before next call is launched.
On the contrary, unless you take special steps to prevent it, a procedure may be "paused" to allow another task to run at almost any time. That's why you need to think carefully about how data is shared between tasks when you code your application.
hakha4 wrote:ZBasic doc says it's not recommended to pass value to task's ByRef.
The reason for the recommendation (and the warning) is that, if not done correctly, the task can end up using a pointer to a variable that no longer exists. This can happen if a task in invoked using a reference to a local variable of a procedure when that procedure terminates (and thus causes the local variable to vanish) prior to the task using the reference.

If you pass a module-level variable to the task, it will exist as long as the program is running so the variable's lifetime is not an issue. You can use the #pragma warning directive to suppress the warning if you know for certain that what you're doing is OK. In retrospect, there may be a way to modify the compiler so that it doesn't issue the warning if the variable passed by reference is defined at the module level.
- Don Kinzer
Post Reply