OpenEdge ABL Memptr Pitfalls

November 30, 2007 · Filed Under Development, Reliability 

Memptr is a very powerful datatype in the ABL/4GL. It allows the programmer to store any type of data including binary. However, as with all dynamic objects in Progress, one must be careful when using it.

Pitfall number one: Scope. Memptrs do not follow the rules of scope to which 4GL programmers have become accustomed.

What does this mean? Why do I care? Well, I’ll tell you. Since Memptrs do not follow the rules of scope, when a variable holding a Memptr HANDLE goes out of scope the associated memory is NOT released. You care because if this happens you now have a memory leak. Every time your program is executed it will leak memory equal to the amount allocated to your memptr.

Pitfall number two is the allocation process. Probably 99% of code you find will do this:

/* define a memptr variable */
def var m as memptr no-undo.
/* Allocate the memory */
set-size(m) = 1024.
/* now go ahead and start using it... */
...

See anything wrong with the above? If not, don’t blame yourself. You are used to Progress doing this for you, but in this case, it does not. What am I referring to? You C programmers will know! The memory that has been allocated to m has not yet been “initialized”. This means the memory will contain random data: whatever happened to be in there before the allocation. If you are using put-string before your first get-string (without the numbytes parameter) then you have nothing to worry about since put-string automatically puts a NULL (0) as the next byte after the string and get-string will only read up to that NULL. But for other operations like put/get-byte or put/get-bytes or put/get-string with the numbytes parameter, grabbing random uninitialized data out of memory could bite you, so beware.

The final pitfall is also related to allocation. In your code, you may define a memptr at the beginning of a procedure and then use it in several places throughout the code. In each use you will want to allocate the appropriate amount of memory. So you may code something like this:

def var m as memptr no-undo.
set-size(m) = 128.
/* do stuff with it here... */
set-size(m) = 1024.
/* do other stuff with it here ... */
and so on...
/* now we clean up and return */
set-size(m) = 0.
return.

This looks great right? We are allocating and cleaning up just as we should, right? Well, yes and no. The pitfall is that the second set-size where we allocate 1024 bytes doesn’t actually allocate anything. It essentially does nothing at all. AND it does not raise an error condition. So now we have a potential bug if the code attempts to put more than 128 bytes into that memptr.

This is solved by setting the memptr to 0 first.

Moral of the story, memptrs need special care as they do not particpate in conventional scoping and cannot be resized until they are cleared.

Hope this helps to save you some time in your coding efforts!

Comments

Leave a Reply

You must be logged in to post a comment.