Skip to content

File unittests_area.c

File List > cubrid > src > executables > unittests_area.c

Go to the documentation of this file

/*
 * Copyright 2008 Search Solution Corporation
 * Copyright 2016 CUBRID Corporation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

/*
 * unittest_area.c : unit tests for area manager
 */

#include "dbtype_def.h"
#include "db_set.h"
#include "porting.h"
#include "lock_free.h"
#include "object_domain.h"
#include "set_object.h"

#include <stdio.h>
#include <pthread.h>
#include <sys/time.h>
#include <assert.h>

#define strlen(s1) ((int) strlen(s1))

#undef SERVER_MODE
/* suppress SERVER_MODE while including client module headers */
#include "class_object.h"
#include "object_template.h"
#define SERVER_MODE

/* areate_create info */
typedef struct area_create_info AREA_CREATE_INFO;
struct area_create_info
{
  const char *name;     /* area name */
  int entry_size;       /* element size */
  int alloc_cnt;        /* alloc count */
};

void *test_area_proc (void *param);
void *test_area_proc_1 (void *param);
void *test_area_proc_2 (void *param);

/* print function */
static struct timeval start_time;

static void
begin (char *test_name)
{
#define MSG_LEN 60
  int i;

  printf ("Testing %s", test_name);
  for (i = 0; i < MSG_LEN - strlen (test_name); i++)
    {
      putchar (' ');
    }
  printf ("...");

  gettimeofday (&start_time, NULL);

#undef MSG_LEN
}

static int
success ()
{
  struct timeval end_time;
  long long int elapsed_msec = 0;

  gettimeofday (&end_time, NULL);

  elapsed_msec = (end_time.tv_usec - start_time.tv_usec) / 1000;
  elapsed_msec += (end_time.tv_sec - start_time.tv_sec) * 1000;

  printf (" %s [%9.3f sec]\n", "OK", (float) elapsed_msec / 1000.0f);
  return NO_ERROR;
}


static int
fail (const char *message)
{
  printf (" %s: %s\n", "FAILED", message);
  assert (false);
  return ER_FAILED;
}

/* thread entry functions */
void *
test_area_proc (void *param)
{
#define NOPS      1000000   /* 1M */

  AREA *area_p = (AREA *) param;
  void *entry = NULL;
  int i, error;

  for (i = 0; i < NOPS; i++)
    {
      if (i % 2 == 0)
    {
      entry = area_alloc (area_p);
      if (entry == NULL)
        {
          pthread_exit ((void *) ER_FAILED);
        }
    }
      else
    {
      error = area_free (area_p, (void *) entry);
      if (error != NO_ERROR)
        {
          pthread_exit ((void *) ER_FAILED);
        }
    }
    }

  pthread_exit ((void *) NO_ERROR);

#undef NOPS
}

void *
test_area_proc_1 (void *param)
{
#define NOPS      1000000   /* 1M */
#define NCACHES   32

  AREA *area_p = (AREA *) param;
  void *entry[NCACHES];
  int idx, i, error;


  for (idx = 0; idx < NCACHES; idx++)
    {
      entry[idx] = NULL;
    }

  idx = 0;
  for (i = 0; i < NOPS; i++)
    {
      if (entry[idx] != NULL)
    {
      error = area_free (area_p, (void *) entry[idx]);
      if (error != NO_ERROR)
        {
          pthread_exit ((void *) ER_FAILED);
        }
      entry[idx] = NULL;
    }

      entry[idx] = area_alloc (area_p);
      if (entry[idx] == NULL)
    {
      pthread_exit ((void *) ER_FAILED);
    }
      idx++;
      if (idx >= NCACHES)
    {
      idx = 0;
    }
    }

  for (i = 0; i < NCACHES; i++)
    {
      if (entry[idx] != NULL)
    {
      error = area_free (area_p, (void *) entry[idx]);
      if (error != NO_ERROR)
        {
          pthread_exit ((void *) ER_FAILED);
        }
      entry[idx] = NULL;
    }
      idx++;
      if (idx >= NCACHES)
    {
      idx = 0;
    }
    }

  pthread_exit ((void *) NO_ERROR);

#undef NCACHES
#undef NOPS
}

void *
test_area_proc_2 (void *param)
{
#define NOPS      1000000   /* 1M */
#define NCACHES   500

  AREA *area_p = (AREA *) param;
  void *entry[NCACHES];
  int idx, i, error;


  for (idx = 0; idx < NCACHES; idx++)
    {
      entry[idx] = NULL;
    }

  for (i = 0; i < NOPS; i++)
    {
      idx = rand () % NCACHES;
      if (entry[idx] != NULL)
    {
      error = area_free (area_p, (void *) entry[idx]);
      if (error != NO_ERROR)
        {
          pthread_exit ((void *) ER_FAILED);
        }
      entry[idx] = NULL;
    }

      entry[idx] = area_alloc (area_p);
      if (entry[idx] == NULL)
    {
      pthread_exit ((void *) ER_FAILED);
    }
    }

  for (idx = 0; idx < NCACHES; idx++)
    {
      if (entry[idx] != NULL)
    {
      error = area_free (area_p, (void *) entry[idx]);
      if (error != NO_ERROR)
        {
          pthread_exit ((void *) ER_FAILED);
        }
      entry[idx] = NULL;
    }
    }

  pthread_exit ((void *) NO_ERROR);

#undef NCACHES
#undef NOPS
}


/* test functions */
static int
test_area (AREA_CREATE_INFO * info, int nthreads, void *(*proc) (void *))
{
#define MAX_THREADS 64
  AREA *area = NULL;
  pthread_t threads[MAX_THREADS];
  char msg[256];
  int i;

  assert (info != NULL);
  sprintf (msg, "%s(size:%d, count:%d), %d threads", info->name, info->entry_size, info->alloc_cnt, nthreads);
  begin (msg);

  /* initialization */
  if (nthreads > MAX_THREADS)
    {
      return fail ("too many threads");
    }

  /* initialization */
  area_init ();

  area = area_create (info->name, info->entry_size, info->alloc_cnt);
  if (area == NULL)
    {
      return fail ("area create fail");
    }

  /* multithreaded test */
  for (i = 0; i < nthreads; i++)
    {
      if (pthread_create (&threads[i], NULL, proc, (void *) area) != NO_ERROR)
    {
      return fail ("thread create");
    }
    }

  for (i = 0; i < nthreads; i++)
    {
      void *retval;

      pthread_join (threads[i], &retval);
      if (retval != NO_ERROR)
    {
      return fail ("thread proc error");
    }
    }

  /* results */
  {
    AREA_BLOCKSET_LIST *blockset;
    AREA_BLOCK *block;
    int i, j, blockset_cnt = 0, block_cnt = 0, chunk_count;
    for (blockset = area->blockset_list; blockset != NULL; blockset = blockset->next)
      {
    for (i = 0; i < blockset->used_count; i++)
      {
        block = blockset->items[i];
        assert (block != NULL);

        chunk_count = CEIL_PTVDIV (block->bitmap.entry_count, LF_BITFIELD_WORD_SIZE);

        for (j = 0; j < chunk_count; j++)
          {
        if (block->bitmap.bitfield[j])
          {
            return fail ("check bitmap status");
          }
          }

        block_cnt++;
      }
    blockset_cnt++;
      }
    printf (" Used %3d blocks(%2d blocksets). ", block_cnt, blockset_cnt);
  }

  /* destory */
  area_destroy (area);
  area_final ();

  return success ();

#undef MAX_THREADS
}

/* program entry */
int
main (int argc, char **argv)
{
  int i, j;

  /* test_cubrid_area */
  {
    AREA_CREATE_INFO cubrid_infos[] = {
      {"Schema templates", sizeof (SM_TEMPLATE), 4}
      ,
      {"Domains", sizeof (TP_DOMAIN), 1024}
      ,
      {"Value containers", sizeof (DB_VALUE), 1024}
      ,
      {"Object templates", sizeof (OBJ_TEMPLATE), 32}
      ,
      {"Assignment templates", sizeof (OBJ_TEMPASSIGN), 64}
      ,
      {"Set references", sizeof (DB_COLLECTION), 1024}
      ,
      {"Set objects", sizeof (COL), 1024}
      ,
      {"Object list links", sizeof (DB_OBJLIST), 4096}
    };

    printf ("============================================================\n");
    printf ("Test simple get/free entry:\n");
    for (j = 0; j < (int) DIM (cubrid_infos); j++)
      {
    for (i = 1; i <= 64; i *= 2)
      {
        if (test_area (&cubrid_infos[j], i, test_area_proc) != NO_ERROR)
          {
        goto fail;
          }
      }
      }

    printf ("============================================================\n");
    printf ("Test get/free entry with cache(32):\n");
    for (j = 0; j < (int) DIM (cubrid_infos); j++)
      {
    for (i = 1; i <= 64; i *= 2)
      {
        if (test_area (&cubrid_infos[j], i, test_area_proc_1) != NO_ERROR)
          {
        goto fail;
          }
      }
      }

    printf ("============================================================\n");
    printf ("Test get/free entry with cache(500), random access:\n");
    for (j = 0; j < (int) DIM (cubrid_infos); j++)
      {
    for (i = 1; i <= 64; i *= 2)
      {
        if (test_area (&cubrid_infos[j], i, test_area_proc_2) != NO_ERROR)
          {
        goto fail;
          }
      }
      }
  }

  /* test different alloc count */
  {
    AREA_CREATE_INFO diff_count_infos[] = {
      {"size 1", 50, 32}
      ,
      {"size 2", 50, 32 * 2}
      ,
      {"size 4", 50, 32 * 4}
      ,
      {"size 8", 50, 32 * 8}
      ,
      {"size 16", 50, 32 * 16}
      ,
      {"size 32", 50, 32 * 32}
      ,
      {"size 64", 50, 32 * 64}
      ,
      {"size 128", 50, 32 * 128}
    };

    printf ("============================================================\n");
    printf ("Test simple get/free entry with different alloc count:\n");
    for (i = 16; i <= 64; i *= 2)
      {
    for (j = 0; j < (int) DIM (diff_count_infos); j++)
      {
        if (test_area (&diff_count_infos[j], i, test_area_proc) != NO_ERROR)
          {
        goto fail;
          }
      }
      }
  }


  /* all ok */
  return 0;

fail:
  printf ("Unit tests failed!\n");
  return ER_FAILED;
}