Thread-safe UDFs

From InterBase

Go Up to Writing a Function Module


In InterBase, the server runs as a single multi-threaded process. This means that you must take some care in the way you allocate and release memory when coding UDFs and in the way you declare UDFs. This section describes how to write UDFs that handle memory correctly in the new single-process environment.

There are several issues to consider when handling memory in the single-process, multi-thread architecture:

  • UDFs must avoid static variables in order to be thread safe. You can use static variables only if you can guarantee that only one user at a time will be accessing UDFs, since users running UDFs concurrently will conflict in their use of the same static memory space. If you do return a pointer to static data, you must not use FREE_IT.
  • UDFs must allocate memory using ib_util_malloc() rather than static arrays in order to be thread-safe. The UDF Declaration employs the "FREE_IT" keyword because memory must be released by the same runtime library that allocated it and "FREE_IT" uses the Visual Studio runtime library, therefore, the memory must be allocated by the Visual Studio runtime library which is facilitated by ib_util_malloc(). Similar problems may occur where ib_util_malloc() is used in a function that does not employ "FREE_IT" in the declaration.
Note:
If malloc() is employed in a UDF for which "FREE_IT" is specified then there will be a mismatch if C++Builder runtime library is used to allocate the memory because Visual Studio runtime library will be used to free it.
Note:
In the case where "FREE_IT" is not specified in the declaration, it is fine to allocate memory using malloc() because memory will be freed by the same runtime library.
  • Memory allocated dynamically is not automatically released, since the process does not end. You must use the FREE_IT keyword when you declare the UDF to the database (DECLARE EXTERNAL FUNCTION).

In the following example for user-defined function FN_LOWER(), the array must be global to avoid going out of context:

Multi-process Version :

char buffer[256];
char *fn_lower(char *ups)
{
. . .
return (buffer);
}

In the following version, the InterBase engine will free the buffer if the UDF is declared using the FREE_IT keyword:

Thread-safe Version:

Notice that this example uses InterBase ib_util_malloc() function to allocate memory.

char *fn_lower(char *ups)
{
 char *buffer = (char *) ib_util_malloc(256);
 ...
 return (buffer);
}

The procedure for allocating and freeing memory for return values in a fashion that is both thread safe and compiler independent is as follows:

1. In the UDF code, use InterBase ib_util_malloc() function to allocate memory for return values. This function is located as follows:

Windows

<<InterBase_home>>/bin/ib_util.dll

Linux

/usr/lib/ib_util.so

Solaris

<<interbase_home>>/lib/ib_util.so

2. Use the FREE_IT keyword in the RETURNS clause when declaring a function that returns dynamically allocated objects. For example:
DECLARE EXTERNAL FUNCTION lowers VARCHAR(256)
RETURNS CSTRING(256) FREE_IT
ENTRY POINT 'fn_lower' MODULE_NAME 'ib_udf'
InterBase FREE_IT keyword allows InterBase users to write thread-safe UDF functions without memory leaks. Note that it is not necessary to provide the extension of the module name.
3. Memory must be released by the same runtime library that allocated it.

Advance To: