Oracle FAQ Your Portal to the Oracle Knowledge Grid
HOME | ASK QUESTION | ADD INFO | SEARCH | E-MAIL US
 

Home -> Community -> Usenet -> c.d.o.misc -> pro*c multithreaded application has memory leak

pro*c multithreaded application has memory leak

From: Jaguk Ku <jkku_at_kynax.com>
Date: Mon, 1 Dec 2003 13:56:28 +0900
Message-ID: <bqef8j$4i8$1@news1.kornet.net>


Hi there,

I made multithreaded application which queries dynamic SQL, it works. But the memory leaks when i query the SQL statement. The more memory leaks, the more i query the SQL statement, even same SQL statement.
I check it with top, shell command.

My machine is SUN E450, Solaris 8. Oracle 9.2.0.1

I changed source code which is from
$(ORACLE_HOME)/precomp/demo/proc/sample10.pc the sample10 doesn't need to be multithreaded. But i think it has to work correctly if i changed it to multithreaded application. the make file and source code will be placed below.

I have to figure out the problem.
Please help

Thanks in advance,

the make file is below

######################################################################
HOME = /user/jkku
ORA = $(ORACLE_HOME) CC = gcc
PROC = proc
LC_INCL  = -I$(HOME)/work/dbmss/libs/include
lc_incl  = include=$(HOME)/work/dbmss/libs/include
SYS_INCL =
sys_incl =
ORA_INCL = -I. \

-I$(ORA)/precomp/public \
-I$(ORA)/rdbms/public \
-I$(ORA)/rdbms/demo \
-I$(ORA)/rdbms/pbsql/public \
-I$(ORA)/network/public \
-DSLMXMX_ENABLE -DSLTS_ENABLE -D_SVID_GETTOD
INCLUDES = $(LC_INCL) $(SYS_INCL) $(ORA_INCL) includes = $(lc_incl) $(sys_incl)
LC_LIBS  =
SYS_LIBS = -lpthread -lsocket -lnsl -lrt
ORA_LIBS = -L$(ORA)/lib/ -lclntsh

LIBS = $(LC_LIBS) $(SYS_LIBS) $(ORA_LIBS) # Define C Compiler flags
CFLAGS += -D_Solaris64_ -m64
CFLAGS += -g -D_REENTRANT

# Define pro*c Compiler flags
PROCFLAGS += THREADS=YES
PROCFLAGS += CPOOL=YES # Our object files
PRECOMPS = sample10.c

OBJS = sample10.o

.SUFFIXES: .o .c .pc

.c.o:
 $(CC) -c $(CFLAGS) $(INCLUDES) $*.c

.pc.c:
 $(PROC) $(PROCFLAGS) $(includes) $*.pc $*.c

all: sample10

sample10: $(PRECOMPS) $(OBJS)
 $(CC) $(CFLAGS) -o sample10 $(OBJS) $(LIBS)

clean:
 rm -rf *.o sample10 sample10.c

######################################################################


the source code is below which i changed the oracle sample10.pc to multithreaded application.

######################################################################
/*******************************************************************
Sample Program 10: Dynamic SQL Method 4

This program connects you to ORACLE using your username and password, then prompts you for a SQL statement. You can enter any legal SQL statement. Use regular SQL syntax, not embedded SQL. Your statement will be processed. If it is a query, the rows fetched are displayed.
You can enter multi-line statements. The limit is 1023 characters. This sample program only processes up to MAX_ITEMS bind variables and MAX_ITEMS select-list items. MAX_ITEMS is #defined to be 40.

*******************************************************************/

#include <stdio.h>
#include <string.h>
#include <setjmp.h>
#include <sqlda.h>

#include <stdlib.h>
#include <sqlcpr.h>

/* Maximum number of select-list items or bind variables. */ #define MAX_ITEMS 40

/* Maximum lengths of the _names_ of the

   select-list items or indicator variables. */

#define MAX_VNAME_LEN     30
#define MAX_INAME_LEN     30

#ifndef NULL

#define NULL 0
#endif

/* Prototypes */
#if defined(__STDC__)
  void sql_error(void);
  int oracle_connect(void);
  int alloc_descriptors(int, int, int);
  int get_dyn_statement(void);
  void set_bind_variables(void);
  void process_select_list(void);
  void help(void);
#else
  void sql_error(/*_ void _*/);
  int oracle_connect(/*_ void _*/);
  int alloc_descriptors(/*_ int, int, int _*/);   int get_dyn_statement(/* void _*/);
  void set_bind_variables(/*_ void -*/);   void process_select_list(/*_ void _*/);   void help(/*_ void _*/);
#endif

char *dml_commands[] = {"SELECT", "select", "INSERT", "insert",

                        "UPDATE", "update", "DELETE", "delete"};

EXEC SQL INCLUDE sqlda;
EXEC SQL INCLUDE sqlca;

EXEC SQL BEGIN DECLARE SECTION;
    char dyn_statement[1024];
    EXEC SQL VAR dyn_statement IS STRING(1024); EXEC SQL END DECLARE SECTION; EXEC ORACLE OPTION (ORACA=YES);
EXEC ORACLE OPTION (RELEASE_CURSOR=YES); SQLDA *bind_dp;
SQLDA *select_dp;

/* Define a buffer to hold longjmp state info. */ jmp_buf jmp_continue;

char *db_uid="dbmuser/dbmuser_at_dbmdb";
sql_context ctx;
int err_sql;

enum{

 SQL_SUCC=0,
 SQL_ERR,
 SQL_NOTFOUND,
 SQL_UNIQUE,
 SQL_DISCONNECT,
 SQL_NOTNULL

};

int main()
{

    int i;

 EXEC SQL ENABLE THREADS;  EXEC SQL WHENEVER SQLERROR DO sql_error();  EXEC SQL WHENEVER NOT FOUND DO sql_not_found();

 /* Connect to the database. */
 if (connect_database() < 0)
  exit(1);

 EXEC SQL CONTEXT USE :ctx;

    /* Process SQL statements. */
    for (;;)
    {

     /* Allocate memory for the select and bind descriptors. */
     if (alloc_descriptors(MAX_ITEMS, MAX_VNAME_LEN, MAX_INAME_LEN) != 0)
         exit(1);

        (void) setjmp(jmp_continue);

        /* Get the statement.  Break on "exit". */
        if (get_dyn_statement() != 0)
            break;

        EXEC SQL PREPARE S FROM :dyn_statement;

        EXEC SQL DECLARE C CURSOR FOR S;

        /* Set the bind variables for any placeholders in the
           SQL statement. */
        set_bind_variables();

        /* Open the cursor and execute the statement.
         * If the statement is not a query (SELECT), the
         * statement processing is completed after the
         * OPEN.
         */

        EXEC SQL OPEN C USING DESCRIPTOR bind_dp;

        /* Call the function that processes the select-list.
         * If the statement is not a query, this function
         * just returns, doing nothing.
         */
        process_select_list();

        /* Tell user how many rows processed. */
        for (i = 0; i < 8; i++)
        {
           if (strncmp(dyn_statement, dml_commands[i], 6) == 0)
           {
               printf("\n\n%d row%c processed.\n", sqlca.sqlerrd[2],
                       sqlca.sqlerrd[2] == 1 ? '\0' : 's');
               break;
           }
        }
        /* Close the cursor. */

  EXEC SQL CLOSE C;   /* When done, free the memory allocated for pointers in the bind and select descriptors. */
     for (i = 0; i < MAX_ITEMS; i++)
     {
         if (bind_dp->V[i] != (char *) 0)
             free(bind_dp->V[i]);
         free(bind_dp->I[i]);   /* MAX_ITEMS were allocated. */
         if (select_dp->V[i] != (char *) 0)
             free(select_dp->V[i]);
         free(select_dp->I[i]); /* MAX_ITEMS were allocated. */
     }

     /* Free space used by the descriptors themselves. */
     SQLSQLDAFree(ctx, bind_dp);
     SQLSQLDAFree(ctx, select_dp);

    } /* end of for(;;) statement-processing loop */  disconnect_database();

    EXEC SQL WHENEVER SQLERROR CONTINUE;     EXEC SQL COMMIT WORK RELEASE;
    puts("\nHave a good day!\n");

    return;
}

/*
 *  Allocate the BIND and SELECT descriptors using sqlald().
 *  Also allocate the pointers to indicator variables
 *  in each descriptor.  The pointers to the actual bind
 *  variables and the select-list items are realloc'ed in
 *  the set_bind_variables() or process_select_list()
 *  routines.  This routine allocates 1 byte for select_dp->V[i]
 *  and bind_dp->V[i], so the realloc will work correctly.
 */

alloc_descriptors(size, max_vname_len, max_iname_len) int size;
int max_vname_len;
int max_iname_len;
{

    int i;

    /*
     * The first sqlald parameter determines the maximum number of
     * array elements in each variable in the descriptor. In
     * other words, it determines the maximum number of bind
     * variables or select-list items in the SQL statement.
     *
     * The second parameter determines the maximum length of
     * strings used to hold the names of select-list items
     * or placeholders.  The maximum length of column
     * names in ORACLE is 30, but you can allocate more or less
     * as needed.
     *
     * The third parameter determines the maximum length of
     * strings used to hold the names of any indicator
     * variables.  To follow ORACLE standards, the maximum
     * length of these should be 30.  But, you can allocate
     * more or less as needed.
     */

    if ((bind_dp =
            SQLSQLDAAlloc(ctx, size, max_vname_len, max_iname_len)) ==
(SQLDA *) 0)
    {
        fprintf(stderr,
            "Cannot allocate memory for bind descriptor.");
        return -1;  /* Have to exit in this case. */
    }

    if ((select_dp =

        SQLSQLDAAlloc(ctx, size, max_vname_len, max_iname_len)) == (SQLDA *) 0)

    {

        fprintf(stderr,
            "Cannot allocate memory for select descriptor.");
        return -1;

    }
    select_dp->N = MAX_ITEMS;

    /* Allocate the pointers to the indicator variables, and the

       actual data. */
    for (i = 0; i < MAX_ITEMS; i++) {

        bind_dp->I[i] = (short *) malloc(sizeof (short));
        select_dp->I[i] = (short *) malloc(sizeof(short));
        bind_dp->V[i] = (char *) malloc(1);
        select_dp->V[i] = (char *) malloc(1);
    }

    return 0;
}

int get_dyn_statement()
{

    char *cp, linebuf[256];
    int iter, plsql;

    for (plsql = 0, iter = 1; ;)
    {

        if (iter == 1)
        {
            printf("\nSQL> ");
            dyn_statement[0] = '\0';
        }

        fgets(linebuf, sizeof linebuf, stdin);

        cp = strrchr(linebuf, '\n');
        if (cp && cp != linebuf)
            *cp = ' ';
        else if (cp == linebuf)
            continue;

        if ((strncmp(linebuf, "EXIT", 4) == 0) ||
            (strncmp(linebuf, "exit", 4) == 0))
        {
            return -1;
        }

        else if (linebuf[0] == '?' ||
            (strncmp(linebuf, "HELP", 4) == 0) ||
            (strncmp(linebuf, "help", 4) == 0))
        {
            help();
            iter = 1;
            continue;
        }

        if (strstr(linebuf, "BEGIN") ||
            (strstr(linebuf, "begin")))
        {
            plsql = 1;
        }

        strcat(dyn_statement, linebuf);

        if ((plsql && (cp = strrchr(dyn_statement, '/'))) ||
            (!plsql && (cp = strrchr(dyn_statement, ';'))))
        {
            *cp = '\0';
            break;
        }
        else
        {
            iter++;
            printf("%3d  ", iter);
        }

    }
    return 0;
}

void set_bind_variables()
{

    int i, n;
    char bind_var[64];

    /* Describe any bind variables (input host variables) */     EXEC SQL WHENEVER SQLERROR DO sql_error();

    bind_dp->N = MAX_ITEMS; /* Initialize count of array elements. */     EXEC SQL DESCRIBE BIND VARIABLES FOR S INTO bind_dp;

    /* If F is negative, there were more bind variables

       than originally allocated by sqlald(). */     if (bind_dp->F < 0)
    {

        printf ("\nToo many bind variables (%d), maximum is %d\n.",
                    -bind_dp->F, MAX_ITEMS);
        return;

    }

    /* Set the maximum number of array elements in the

       descriptor to the number found. */     bind_dp->N = bind_dp->F;

    /* Get the value of each bind variable as a
     * character string.
     *
     * C[i] contains the length of the bind variable
     *      name used in the SQL statement.
     * S[i] contains the actual name of the bind variable
     *      used in the SQL statement.
     *
     * L[i] will contain the length of the data value
     *      entered.
     *
     * V[i] will contain the address of the data value
     *      entered.
     *
     * T[i] is always set to 1 because in this sample program
     *      data values for all bind variables are entered
     *      as character strings.
     *      ORACLE converts to the table value from CHAR.
     *
     * I[i] will point to the indicator value, which is
     *      set to -1 when the bind variable value is "null".
     */

    for (i = 0; i < bind_dp->F; i++)
    {
        printf ("\nEnter value for bind variable %.*s:  ",
               (int)bind_dp->C[i], bind_dp->S[i]);
        fgets(bind_var, sizeof bind_var, stdin);

        /* Get length and remove the new line character. */
        n = strlen(bind_var) - 1;

        /* Set it in the descriptor. */
        bind_dp->L[i] = n;

        /* (re-)allocate the buffer for the value.
           sqlald() reserves a pointer location for
           V[i] but does not allocate the full space for
           the pointer. */

  bind_dp->V[i] = (char *) realloc(bind_dp->V[i], (bind_dp->L[i] + 1));
        /* And copy it in. */
        strncpy(bind_dp->V[i], bind_var, n);

        /* Set the indicator variable's value. */
        if ((strncmp(bind_dp->V[i], "NULL", 4) == 0) ||
                (strncmp(bind_dp->V[i], "null", 4) == 0))
            *bind_dp->I[i] = -1;
        else
            *bind_dp->I[i] = 0;

        /* Set the bind datatype to 1 for CHAR. */
        bind_dp->T[i] = 1;

    }

 return;
}

void process_select_list()
{

    int i, null_ok, precision, scale;

    if ((strncmp(dyn_statement, "SELECT", 6) != 0) &&

        (strncmp(dyn_statement, "select", 6) != 0))     {

        select_dp->F = 0;
        return;

    }

    /* If the SQL statement is a SELECT, describe the

        select-list items.  The DESCRIBE function returns
        their names, datatypes, lengths (including precision
        and scale), and NULL/NOT NULL statuses. */

    select_dp->N = MAX_ITEMS;

    EXEC SQL DESCRIBE SELECT LIST FOR S INTO select_dp;

    /* If F is negative, there were more select-list

       items than originally allocated by sqlald(). */     if (select_dp->F < 0)
    {

        printf ("\nToo many select-list items (%d), maximum is %d\n",
                -(select_dp->F), MAX_ITEMS);
        return;

    }

    /* Set the maximum number of array elements in the

       descriptor to the number found. */     select_dp->N = select_dp->F;

    /* Allocate storage for each select-list item.

       sqlprc() is used to extract precision and scale
       from the length (select_dp->L[i]).

       sqlnul() is used to reset the high-order bit of
       the datatype and to check whether the column
       is NOT NULL.

       CHAR    datatypes have length, but zero precision and
               scale.  The length is defined at CREATE time.

       NUMBER  datatypes have precision and scale only if
               defined at CREATE time.  If the column
               definition was just NUMBER, the precision
               and scale are zero, and you must allocate
               the required maximum length.

       DATE    datatypes return a length of 7 if the default
               format is used.  This should be increased to
               9 to store the actual date character string.
               If you use the TO_CHAR function, the maximum
               length could be 75, but will probably be less
               (you can see the effects of this in SQL*Plus).

       ROWID   datatype always returns a fixed length of 18 if
               coerced to CHAR.

       LONG and
       LONG RAW datatypes return a length of 0 (zero),
               so you need to set a maximum.  In this example,
               it is 240 characters.

       */

    printf ("\n");
    for (i = 0; i < select_dp->F; i++)
    {

        char title[MAX_VNAME_LEN];
        /* Turn off high-order bit of datatype (in this example,
           it does not matter if the column is NOT NULL). */
        sqlnul ((unsigned short *)&(select_dp->T[i]), (unsigned short
*)&(select_dp->T[i]), &null_ok);
        switch (select_dp->T[i])
        {
            case  1 : /* CHAR datatype: no change in length
                         needed, except possibly for TO_CHAR
                         conversions (not handled here). */
                break;
            case  2 : /* NUMBER datatype: use sqlprc() to
                         extract precision and scale. */
                sqlprc ((unsigned int *)&(select_dp->L[i]), &precision,
&scale);
                      /* Allow for maximum size of NUMBER. */
                if (precision == 0) precision = 40;
                      /* Also allow for decimal point and
                         possible sign. */
                /* convert NUMBER datatype to FLOAT if scale > 0,
                   INT otherwise. */
                if (scale > 0)
                    select_dp->L[i] = sizeof(float);
                else
                    select_dp->L[i] = sizeof(int);
                break;

            case  8 : /* LONG datatype */
                select_dp->L[i] = 240;
                break;

            case 11 : /* ROWID datatype */
            case 104 : /* Universal ROWID datatype */
                select_dp->L[i] = 18;
                break;

            case 12 : /* DATE datatype */
                select_dp->L[i] = 9;
                break;

            case 23 : /* RAW datatype */
                break;

            case 24 : /* LONG RAW datatype */
                select_dp->L[i] = 240;
                break;
        }
        /* Allocate space for the select-list data values.
           sqlald() reserves a pointer location for
           V[i] but does not allocate the full space for
           the pointer.  */

        if (select_dp->T[i] != 2)
           select_dp->V[i] = (char *) realloc(select_dp->V[i],
                                    select_dp->L[i] + 1);
        else
           select_dp->V[i] = (char *) realloc(select_dp->V[i],
                                    select_dp->L[i]);

        /* Print column headings, right-justifying number
            column headings. */

        /* Copy to temporary buffer in case name is null-terminated */
        memset(title, ' ', MAX_VNAME_LEN);
        strncpy(title, select_dp->S[i], select_dp->C[i]);
        if (select_dp->T[i] == 2)
           if (scale > 0)
             printf ("%.*s ", select_dp->L[i]+3, title);
           else
             printf ("%.*s ", select_dp->L[i], title);
        else
          printf("%-.*s ", select_dp->L[i], title);

        /* Coerce ALL datatypes except for LONG RAW and NUMBER to
           character. */
        if (select_dp->T[i] != 24 && select_dp->T[i] != 2)
            select_dp->T[i] = 1;

        /* Coerce the datatypes of NUMBERs to float or int depending on
           the scale. */
        if (select_dp->T[i] == 2)
          if (scale > 0)
             select_dp->T[i] = 4;  /* float */
          else
             select_dp->T[i] = 3;  /* int */
    }
    printf ("\n\n");

    /* FETCH each row selected and print the column values. */     EXEC SQL WHENEVER NOT FOUND GOTO end_select_loop;

    for (;;)
    {

        EXEC SQL FETCH C USING DESCRIPTOR select_dp;

        /* Since each variable returned has been coerced to a
           character string, int, or float very little processing
           is required here.  This routine just prints out the
           values on the terminal. */
        for (i = 0; i < select_dp->F; i++)
        {
            if (*select_dp->I[i] < 0)
                if (select_dp->T[i] == 4)
                  printf ("%-*c ",(int)select_dp->L[i]+3, ' ');
                else
                  printf ("%-*c ",(int)select_dp->L[i], ' ');
            else
                if (select_dp->T[i] == 3)     /* int datatype */
                  printf ("%*d ", (int)select_dp->L[i],
                                 *(int *)select_dp->V[i]);
                else if (select_dp->T[i] == 4)     /* float datatype */
                  printf ("%*.2f ", (int)select_dp->L[i],
                                 *(float *)select_dp->V[i]);
                else                          /* character string */
                  printf ("%-*.*s ", (int)select_dp->L[i],
                            (int)select_dp->L[i], select_dp->V[i]);
        }
        printf ("\n");

    }
end_select_loop:

    return;
}

void help()
{

    puts("\n\nEnter a SQL statement or a PL/SQL block at the SQL> prompt.");
    puts("Statements can be continued over several lines, except");
    puts("within string literals.");
    puts("Terminate a SQL statement with a semicolon.");
    puts("Terminate a PL/SQL block (which can contain embedded
semicolons)");
    puts("with a slash (/).");
    puts("Typing \"exit\" (no semicolon needed) exits the program.");
    puts("You typed \"?\" or \"help\" to get this message.\n\n");
}

int connect_database()
{
 err_sql = SQL_SUCC;
 EXEC SQL WHENEVER SQLERROR DO sql_error();  EXEC SQL WHENEVER NOT FOUND DO sql_not_found();  EXEC SQL CONTEXT ALLOCATE :ctx;
 EXEC SQL CONTEXT USE :ctx;
 EXEC SQL CONNECT :db_uid;

 if(err_sql != SQL_SUCC){
  printf("err => connect database(ctx:%ld, uid:%s) failed!\n", ctx, db_uid);   return -1;
 }

 return 1;
}

int disconnect_database()
{
 err_sql = SQL_SUCC;
 EXEC SQL WHENEVER SQLERROR DO sql_error();  EXEC SQL WHENEVER NOT FOUND DO sql_not_found();  EXEC SQL CONTEXT USE :ctx;
 EXEC SQL COMMIT WORK RELEASE;
 EXEC SQL CONTEXT FREE:ctx;

 return 1;
}

void sql_error()
{

 printf("err => %.*s", sqlca.sqlerrm.sqlerrml, sqlca.sqlerrm.sqlerrmc);
 printf("in \"%.*s...\'\n", oraca.orastxt.orastxtl, oraca.orastxt.orastxtc);
 printf("on line %d of %.*s.\n\n", oraca.oraslnr, oraca.orasfnm.orasfnml,
oraca.orasfnm.orasfnmc);

 switch(sqlca.sqlcode) {
  case -1: /* unique constraint violated */    err_sql = SQL_UNIQUE;
   break;

  case -1012:     /* not logged on */
  case -1089:
  case -3133:
  case -1041:
  case -3114:
  case -3113:
   /* ¿À¶óŬÀÌ shutdownµÇ°Å³ª ·Î±×ÀÎ »óÅ°¡ ¾Æ´Ò¶§ ÀçÁ¢¼Ó ½Ãµµ */
   /* immediate shutdown in progress - no operations are permitted */
   /* end-of-file on communication channel */    /* internal error. hostdef extension doesn't exist */    err_sql = SQL_DISCONNECT;
   break;
  case -1400:
   err_sql = SQL_NOTNULL;
   break;
  default:
   err_sql = SQL_ERR;
   break;
 }

 EXEC SQL CONTEXT USE :ctx;
 EXEC SQL WHENEVER SQLERROR CONTINUE;
 EXEC SQL ROLLBACK WORK;
}

void sql_not_found()
{
 err_sql = SQL_NOTFOUND;
}

######################################################################
Received on Sun Nov 30 2003 - 22:56:28 CST

Original text of this message

HOME | ASK QUESTION | ADD INFO | SEARCH | E-MAIL US