LongJmp() and Objects

Discussion of issues related specifically to writing code for native mode devices. This includes ZBasic code as well as assembly language code and C code, both inline and standalone.
Post Reply
wwwoholic
Posts: 53
Joined: 23 December 2010, 20:58 PM

LongJmp() and Objects

Post by wwwoholic »

From the system library manual: "this routine should not be used in applications that use ZBasic objects because it bypasses the execution of destructors that are necessary for proper object management".

My program does not allocate objects dynamically, all of them are module-level variables.

There is a subroutine that is called on fatal errors from many places in the program. This subroutine used to "blink out" error code in forever loop. However customer requested a way to manually reset error condition. The least cumbersome way to do it is long jump to somewhere near the beginning of the program. CPU reset is not a good option because there are many connected devices with established serial channel communication that won't be happy to lose stream of commands from controller.

Can I use LongJmp() in these conditions?
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Re: LongJmp() and Objects

Post by dkinzer »

wwwoholic wrote:Can I use LongJmp() in these conditions?
Probably. However, an additional caveat is that none of the stacked procedures may have local allocated string types. This caveat includes structures containing allocated strings as well.

Although the description of LongJmp() doesn't mention this issue specifically, the reason for the restriction is exactly the same, i.e. the LongJmp() bypasses the normal procedure exit that deallocates local allocated storage. If any of the stacked procedures need to use strings you'll have to change them to BoundedString types or define the string variables at the module level.

Here is a simple test case to demonstrate the effect of using local allocated strings with LongJmp(). If you compile and execute the program as given, you'll note that the heap headroom decreases with each iteration. In contrast, if you comment out the definition of USE_ALLOC_STRING the heap headroom decreases initially and then remains steady.

Code: Select all

#define USE_ALLOC_STRING

Dim jmpBuf(System.JumpBufSize) as Byte
Dim count as UnsignedInteger

Sub Main()
   count = 0
   If &#40;SetJmp&#40;jmpBuf&#41; < 20&#41; Then
      Debug.Print "Heap headroom is "; System.HeapHeadRoom&#40;&#41;
      Call Level_1&#40;100 + CByte&#40;count&#41;&#41;
      Debug.Print "back from Level_1"
   End If
End Sub

Sub Level_1&#40;ByVal v as Byte&#41;
   Call Level_2&#40;v&#41;
   Debug.Print "back from Level_2"
End Sub

Sub Level_2&#40;ByVal v as Byte&#41;
   Call Level_3&#40;v&#41;
   Debug.Print "back from Level_3"
End Sub

Sub Level_3&#40;ByVal v as Byte&#41;
#if defined&#40;USE_ALLOC_STRING&#41;
   Dim s as String
#else
   Dim s as BoundedString&#40;10&#41;
#endif
	
   s = CStr&#40;v&#41;
   Debug.Print s
   count = count + 1
   Call LongJmp&#40;jmpBuf, count&#41;
End Sub
- Don Kinzer
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

I've modified the test case to illustrate a third possibility for handling string storage, i.e. to use static local variables. This case is essentially identical to using module-level variables except that the visibility is restricted to the procedure containing the definition.

To prevent heap leakage, the test program must be compiled with USE_ALLOC_STRING not defined or either USE_STATIC_STRING defined or USE_LOCAL_STRING undefined.

Code: Select all

#define USE_ALLOC_STRING
#define USE_LOCAL_STRING
'#define USE_STATIC_STRING

Dim jmpBuf&#40;System.JumpBufSize&#41; as Byte
Dim count as UnsignedInteger

#if Not defined&#40;USE_LOCAL_STRING&#41;
  #if defined&#40;USE_ALLOC_STRING&#41;
   Private s as String
  #else
   Private s as BoundedString&#40;10&#41;
  #endif
#endif

Sub Main&#40;&#41;
   count = 0
   If &#40;SetJmp&#40;jmpBuf&#41; < 20&#41; Then
      Debug.Print "Heap headroom is "; System.HeapHeadRoom&#40;&#41;
      Call Level_1&#40;100 + CByte&#40;count&#41;&#41;
      Debug.Print "back from Level_1"
   End If
End Sub

Sub Level_1&#40;ByVal v as Byte&#41;
   Call Level_2&#40;v&#41;
   Debug.Print "back from Level_2"
End Sub

Sub Level_2&#40;ByVal v as Byte&#41;
   Call Level_3&#40;v&#41;
   Debug.Print "back from Level_3"
End Sub

Sub Level_3&#40;ByVal v as Byte&#41;
#if defined&#40;USE_LOCAL_STRING&#41;
  #if defined&#40;USE_STATIC_STRING&#41;
    #if defined&#40;USE_ALLOC_STRING&#41;
   Static s as String
    #else
   Static s as BoundedString&#40;10&#41;
    #endif
  #else
    #if defined&#40;USE_ALLOC_STRING&#41;
   Dim s as String
    #else
   Dim s as BoundedString&#40;10&#41;
    #endif
  #endif
#endif
	
   s = CStr&#40;v&#41;
   Debug.Print s
   count = count + 1
   Call LongJmp&#40;jmpBuf, count&#41;
End Sub
- Don Kinzer
wwwoholic
Posts: 53
Joined: 23 December 2010, 20:58 PM

Post by wwwoholic »

Hi Don,

In the sub level_3 why one of the bounded string defined as static? Isn't being bounded enough for being safe? Or is it there just to complete all the #defs?

Do I understand correctly that local bounded strings are allocated in the stack?

The only place in my program where local string is defined does not call jump, but I added bounded definition there anyway, just in case something called from there would.

Another question is how implicit string allocation is handled with concatenation operators, especially if there are no actual variables defined to hold the result. For example:

Code: Select all

call mysub&#40;"value=" & value&#41;
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

wwwoholic wrote:In the sub level_3 why one of the bounded string defined as static? Isn't being bounded enough for being safe?
Yes, begin a BoundedString is sufficient. I did it that way for symmetry.
wwwoholic wrote:Do I understand correctly that local bounded strings are allocated in the stack?
All variable defined within a procedure reside on the stack unless they have the Static attribute.
wwwoholic wrote:Another question is how implicit string allocation is handled with concatenation operators, especially if there are no actual variables defined to hold the result.
Strings like that are temporary strings, and work just like allocated strings. You'll have the same issue if you pass one of those to a subroutine that might execute LongJmp(). To get around that you'll have to store the fabricated string in a BoundedString or a statically allocated string and then pass that intermediate string variable to the called routine.
- Don Kinzer
dkinzer
Site Admin
Posts: 3120
Joined: 03 September 2005, 13:53 PM
Location: Portland, OR

Post by dkinzer »

dkinzer wrote:To get around that you'll have to store the fabricated string in a BoundedString or a statically allocated string and then pass that intermediate string variable to the called routine.
Here is an test case that demonstrates the problem and solution. With USE_DIRECT_PARAMETER defined the call to Level_3() is made with a parameter that creates a temporary string. Without it defined, the string to be passed is first stored in a BoundedString.

Note that the string concatenation used in this test case isn't the issue specifically. Using CStr() alone is problematic because it returns an allocated string (in most cases). Storing the string result in a BoundedString causes that temporary string to be deleted immediately.

Code: Select all

#define USE_DIRECT_PARAMETER

Dim jmpBuf&#40;System.JumpBufSize&#41; as Byte
Dim count as UnsignedInteger

Sub Main&#40;&#41;
   count = 0
   If &#40;SetJmp&#40;jmpBuf&#41; < 20&#41; Then
      Debug.Print "Heap headroom is "; System.HeapHeadRoom&#40;&#41;
      Call Level_1&#40;100 + CByte&#40;count&#41;&#41;
      Debug.Print "back from Level_1"
   End If
End Sub

Sub Level_1&#40;ByVal v as Byte&#41;
   Call Level_2&#40;v&#41;
   Debug.Print "back from Level_2"
End Sub

Sub Level_2&#40;ByVal v as Byte&#41;
#if defined&#40;USE_DIRECT_PARAMETER&#41;
   Call Level_3&#40;"&#91;" & CStr&#40;v&#41; & "&#93;"&#41;
#else
   Dim s as BoundedString&#40;10&#41;
   s = "&#91;" & CStr&#40;v&#41; & "&#93;"
   Call Level_3&#40;s&#41;
#endif
   Debug.Print "back from Level_3"
End Sub

Sub Level_3&#40;ByVal s as String&#41;
   Debug.Print s
   count = count + 1
   Call LongJmp&#40;jmpBuf, count&#41;
End Sub
- Don Kinzer
Post Reply