Hello Guest it is April 19, 2024, 10:18:01 AM

Author Topic: Reentrant code  (Read 13456 times)

0 Members and 1 Guest are viewing this topic.

Reentrant code
« on: March 03, 2008, 05:52:06 AM »
VB functions and subroutines should allocate local variables on the stack so that they can be called reentrantly. Cypress doesn't seem to be doing this. My current code is rather long to post here, but essentially I'm trying to do something like this:

sub divide(byval a as double, byval b as double)
   Dim c as double
   if abs(b-a)>0.1 then
    c=(a+b)/2
    divide (a,c)
    divide (c,b)
  else
    Code "G1 X" & b
  end if
end sub

What happens is that, after the first recursive call to divide(a,c), c gets set to 0 so the second call is divide(0,d), not divide(c,d). Indeed, the values of all local variables get reset to 0 after the first recursive call. I could recalculate them but that really shouldn't be necessary.
Have I missed something you need to do in VB to make routines recursive? Or is this a bug?
Thanks for any input
Chris Lusby Taylor

Offline stirling

*
  • *
  •  2,188 2,188
  • UK
    • View Profile
    • www.razordance.co.uk
Re: Reentrant code
« Reply #1 on: March 03, 2008, 07:59:03 AM »
Hi Chris

Nice catch - In my view, you're absolutely correct - unless it's documented somewhere that VBA intentionally disobeys accepted standards for local variables - which I doubt very much. Clearly the folks at Cypress push 'n' pop the params correctly but not the standard locals.
This really is unacceptable. A while back I was having all sorts of strange behaviour in a probing routine, which I just couldn't make sense of and eventually had to totally code round it. I'm now thinking that CypressVB is simply not to be trusted - particularly in a 'safety critical' machine tool environment. I wonder what Brian's thoughts are on this - there must be other (correctly written) VB plugins out there that Mach could use instead.

Cheers

Ian
Re: Reentrant code
« Reply #2 on: March 03, 2008, 12:43:03 PM »
Dont blame Cypress. The problem is in the way Art linked VBA into mach. I dont know all the details, but I have had several long talks with Brian about it, and he has been studying the Cypress docs and the code and he thinks there is hope we can get a full VBA implementation in mach. He has also looked at using the CYpress option to pre-complie the code, so we can have faster and more reliable operation. Most importantly, we may have  a single VB context for each screenset- therefore could use subs across buttons, common declares, etc.

It will be a while before he gets it done, but I know he is very actively looking into how to fix the whole VB interconnection to mach.
Re: Reentrant code
« Reply #3 on: March 03, 2008, 02:37:08 PM »
I wasn't meaning to blame Cypress, but it's important to know that functions and subroutines that have local variables are not reentrant. That's a major piece of functionality that you would assume. Luckily, in this instance, debugging quickly showed me the problem, but you could well imagine scenarios where this would create a very obscure bug.

One possible workaround for now would seem to be to declare more parameters than you need. Just use the spares as local variables.

So, I would like to encourage Brian to work on making the code truly reentrant. And, making a single VB context across all the buttons and other objects in a screen, or screenset, would seem hugely beneficial, too. Perhaps the screenset could own a common file whose objects could be accessed from any button or other control.

I posted another email recently on the VB-Mach3 interaction (on how the execution order of multiple Code "M98 (my.tap)" is reversed) and I imagine the issues are related.

Chris

Offline stirling

*
  • *
  •  2,188 2,188
  • UK
    • View Profile
    • www.razordance.co.uk
Re: Reentrant code
« Reply #4 on: March 04, 2008, 04:36:53 AM »
Hi Ron
I obviously don't know the full issues you've discussed with Brian about Cypress's Enable VB, but this particular issue at least is surely nothing to do with the way it has been linked into Mach. This is an error in the way the the interpreter's code generator builds its stack frames.
I contacted Cypress and had them send me an evaluation copy of their latest version of Enable and even running standalone it makes exactly the same error. I've asked Cypress for their comments and await their reply.
Cheers
Ian

Offline stirling

*
  • *
  •  2,188 2,188
  • UK
    • View Profile
    • www.razordance.co.uk
Re: Reentrant code
« Reply #5 on: March 05, 2008, 02:48:25 AM »
Chris: Cypress got back to me and asked if I could send an example of what I/we thought was wrong. I did this but then they contacted me again and said they couldn't book any support time to look at it as I wasn't the licenced developer. ???

One for Brian when he has time I guess. Anyone know how to escalate this onto his list?

Cheers

Ian
« Last Edit: March 05, 2008, 02:58:24 AM by stirling »

Offline stirling

*
  • *
  •  2,188 2,188
  • UK
    • View Profile
    • www.razordance.co.uk
Re: Reentrant code
« Reply #6 on: March 05, 2008, 01:50:06 PM »
Another update: here's a copy of the email text I just received from Cypress:

"I spoke to an engineer about this.  Cypress Enable does not fully support recursion, that is one of the limitations of Enable and will not likely change in any new releases.  You will need to code around this limitation."

So I guess that's it - shame - especially as to implement the local vars correctly is actually pretty trivial - especially when they've already done the "by value" parameter mechanism. Hmmmmm - thinks.... wonder if they implemented "by reference"....
Re: Reentrant code
« Reply #7 on: March 10, 2008, 10:52:05 PM »
Well, it's good to know that Cypress admit they don't 'fully' support recursion. I will attempt to do their job for them by describing two different ways you can work around the problem.
NOTE: I haven't tested the following code fragments. The syntax may well be wrong. I am composing this email late at night on a different machine from the one I run Mach3 on, which I keep deliberately clean.

1. Use dummy parameters
For all local variables you want to be able to use in a recursively-called sub or function, declare the variable as a parameter.
This is a bit messy in that you have to pass in some, unused, value for the parameter every time you call it, but this can be avoided by having two routines: a top-level one you call only from outside itself, and a recursively-callable one.
For instance, going back to my original problem, I want:

sub divide(byval a as double, byval b as double)
   Dim c as double
   if abs(b-a)>0.1 then
    c=(a+b)/2
    call divide (a,c)
    call divide (c,b)
  else
    Code "G1 X" & b
  end if
end sub
...
call divide(10,20)

..This doesn't work in Cypress VB because the second recursive call won't have the correct value for c. But this should:

sub divide(byval a as double, byval b as double, byval c as double)
   if abs(b-a)>0.1 then
    c=(a+b)/2
    call divide (a,c,0) 'The value of the third parameter is arbitrary. Might as well make it 0
    call divide (c,b,0)
  else
    Code "G1 X" & b
  end if
end sub
...
call divide(10,20,0) 'Again, the value(s) of the parameters that are really local variables is arbitrary so I use 0

But, if you need a lot of locals this gets cumbersome, as calls have to have lots of trailing ,0,0,0,..., so you could hide it:

sub divide(byval a as double, byval b as double)
   call divideRecurse(a,b,0)
end sub

sub divideRecurse(byval a as double, byval b as double, byval c as double)
   if abs(b-a)>0.1 then
    c=(a+b)/2
    call divideRecurse (a,c,0) 'The value of the third parameter is arbitrary. Might as well make it 0
    call divideRecurse (c,b,0)
  else
    Code "G1 X" & b
  end if
end sub
...
call divide(10,20) 'top-level calls don't need any extra parameters


2. Use an array of parameters:

Dim c(10) as double 'assuming 11 is the deepest recursion needed, declare an array for each local variable
'
sub divide(byval a as double, byval b as double, byval level as integer)
   if abs(b-a)>0.1 then
    c(level)=(a+b)/2  'You must use only this level's copy of the local parameters
    call divide (a,c, level+1)  'The third parameter tells the interpreter which set of locals to use
    call divide (c,b, level+1)
  else
    Code "G1 X" & b
  end if
end sub
...
call divide(10,20,0) 'You only need one extra parameter. It must be set to 0 or 1 in top-level calls

Possibly, these techniques will already be known and obvious to anyone who uses recursion, but as nobody had previously raised this whole subject, this may not be as many people as I thought.

Recursion can be a very elegant way to encode the logic of certain operations involving trees or, as in my example, the need to divide a big task into smaller ones until the task is so small it can be performed simply without unacceptable error. The actual code I was using this for, by the way, machines Bezier curves by dividing them into tiny fragments, each of which can be approximated by a straight G1 move. This is an ideal subject for recursion.

Any queries, just ask me.
Chris


Offline stirling

*
  • *
  •  2,188 2,188
  • UK
    • View Profile
    • www.razordance.co.uk
Re: Reentrant code
« Reply #8 on: March 11, 2008, 08:24:32 AM »
never needed to code round recursion cos I never came accross a high level language this side of the 1960's that couldn't do it. Actually that's not quite true because if you get into really deep recursion, then most stacks will fail sooner or later. You're kind of doing this with your array but a generalized method is to create/use your own heap based stack.