Out of Scope but Not Out of Mind
By Galen Carpenter, Senior Systems Software Engineer
Synergy DBL is a very flexible language. This is almost always a good thing, because it gives you a huge range of options as you develop your applications. However, this same flexibility can also allow you to write code with unforeseen consequences. The compiler has been enhanced to point out many coding problems, but it does not prevent all bad coding practices at runtime. A few of these runtime problems can occur when using SQL Connection or the routine call block API and the memory attached to the usage of these goes out of scope.
There are four types of memory that can go out of scope:
- Stack records, which go out of scope when the routine returns.
- Local records and literals, which go out of scope on Windows and UNIX when the routine is swapped out.
- %MEM_PROC memory, which goes out of scope when it is explicitly released or, if the %MEM_PROC memory has not been created with the DM_STATIC flag, when the routine returns.
- Object memory (dynamic class fields), which goes away when the object is deleted.
Most of the time this is not a problem, as the references to the memory have also already been removed, but there are a couple of ways references to the memory can remain after the memory has been released.
With the routine call block API, memory from variables is set up as parameters for an RCB call with RCB_SETARG, RCB_SETARGS, or RCB_INSARG. With SQL Connection, memory from variables is bound to an SQL cursor with %SSC_BIND, %SSC_REBIND, and %SSC_SQLLINK. If the memory from any of these variables has been released or has gone out of scope when the %RCB_CALL or %SSC_MOVE is performed, the memory these variables was referencing is updated or accessed by the %RCB_CALL or %SCC_MOVE, but that memory could now be used for other purposes. This can cause program execution to change in unexpected ways or result in segmentation faults or access violations.
As an example, let’s say routine FOO calls %RCB_CREATE, then passes the routine call block identifier (RCB ID) to the SETUP_ARGS function, which sets up the arguments and returns. Then routine FOO uses %RCB_CALL to perform the call. If routine SETUP_ARGS sets the arguments using %MEM_PROC memory but does not specify DM_STATIC, the %MEM_PROC memory will be released when routine SETUP_ARGS returns. However, if STACK record variables are specified instead of using %MEM_PROC memory, the stack memory goes away when the routine returns. If LOCAL record variables are specified, the memory does not necessarily go away when the routine returns, but it will go away any time memory reclamation is performed—potentially causing any number of problems that may not be reproducible. Similar problems can occur with SQL Connection.
In the 10.1.1b patch, we added checking to identify some of these types of situations, and the 10.1.1c patch contains even more checking. When you compile with -qcheck, runtime checking is performed in the compiled routines to identify when the memory associated with the SQL cursor or RCB ID goes out of scope, so that the runtime will issue one of the new errors (RCBSTACK, RCBDYN, RCBREL, RCBOBJ, SQLOBJ, or SQLDYN) or an existing error (SQLSTACK or SQLREL). This is another excellent reason to compile with -qcheck when doing development.
It is possible for these new errors to be issued when there is not a functional problem. Sometimes, an RCB ID is created with %RCB_CREATE, then is passed repeatedly to a routine that sets the arguments to stack or local variables, does the %RCB_CALL, and returns. In this case, the checking will report an error even though functionally there is no problem. To prevent this situation, use RCB_SETARG to set each problem argument to a literal value after the %RCB_CALL.
To correctly identify where the problems occur, it is important that all of the sources be compiled with -qcheck. If the routine where the error occurred is not compiled with -qcheck, the error will not be detected there, but it may be detected in another routine that was compiled with -qcheck. For example, if stack variables are used in routine A, the same memory may be used as stack memory in routine B, and the error will be detected when routine B returns. This type of scenario can lead to loss of hair when looking for the source of the error.
For the RCB errors, the argument number, the name of the RCB routine, and the location of the %RCB_CREATE are now displayed as part of the error text, as well as whether the DM_STATIC option was specified. This information makes it much easier to locate the cause of the problem in a program.