/***************************************************************************
 *
 * Copyright (c) 2000-2001 BalaBit IT Ltd, Budapest, Hungary
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: freeq.c,v 1.3.4.4 2001/09/24 17:38:15 bazsi Exp $
 *
 ***************************************************************************/

#include <zorp/freeq.h>
#include <zorp/log.h>

#define FREE_QUEUE_SIZE 16
#define FREE_QUEUE_NEXT_SLOT(p) ((p < FREE_QUEUE_SIZE-1) ? p + 1 : 0)

static ZFreeQueue *freeq;

typedef struct _ZFreeQueueItem
{
  gpointer f;
  ZFreeFunc freefn;
} ZFreeQueueItem;

typedef struct _ZRealFreeQueue 
{
  GMutex *lock;
  GCond *queue_flushed;
  ZFreeQueueItem *free_queue;
  gint first_free, free_queue_size;
} ZRealFreeQueue;

static ZFreeQueue *
z_free_queue_new(void)
{
  ZRealFreeQueue *self = g_new0(ZRealFreeQueue, 1);
  
  self->lock = g_mutex_new();
  self->queue_flushed = g_cond_new();
  self->free_queue_size = 64;
  self->free_queue = g_new0(ZFreeQueueItem, self->free_queue_size);
  return (ZFreeQueue *) self;
}

static void
z_free_queue_free(ZFreeQueue *s)
{
  ZRealFreeQueue *self = (ZRealFreeQueue *) s;
  
  g_mutex_free(self->lock);
  g_cond_free(self->queue_flushed);
  g_free(self);
}

static void
z_free_queue_add_item(ZFreeQueue *s, gpointer f, ZFreeFunc freefn)
{
  ZRealFreeQueue *self = (ZRealFreeQueue *) s;
  
  g_mutex_lock(self->lock);
  if (self->first_free >= self->free_queue_size)
    {
      /*LOG
        This message indicates that the free queue - used for freeing up
        structures in a synchronized manner - is full. Zorp automatically
        resizes the free queue so this message is harmless. The old size
        of the free queue is reported, the new one will be twice as big.
       */
      z_log(NULL, CORE_DEBUG, 5, "Free queue full resizing free queue; freeq_size='%d'", self->free_queue_size);
      self->free_queue = g_renew(ZFreeQueueItem, self->free_queue, self->free_queue_size * 2);
      self->free_queue_size *= 2;
    }
  self->free_queue[self->first_free].f = f;
  self->free_queue[self->first_free].freefn = freefn;
  self->first_free++;
  g_mutex_unlock(self->lock);
}

static void
z_free_queue_flush_items(ZFreeQueue *s)
{
  ZRealFreeQueue *self = (ZRealFreeQueue *) s;
  ZFreeQueueItem *items;
  gint i, num;

  g_mutex_lock(self->lock);
  num = self->first_free;
  items = alloca(sizeof(ZFreeQueueItem) * num);
  memcpy(items, self->free_queue, num * sizeof(ZFreeQueueItem));
  self->first_free = 0;
  g_cond_signal(self->queue_flushed);
  g_mutex_unlock(self->lock);

  for (i = 0; i < num; i++)
    {
      if (items[i].freefn)
        items[i].freefn(items[i].f);
      else
        g_free(items[i].f);
    }
}

void
z_free_queue_init(void)
{
  freeq = z_free_queue_new();
}

void
z_free_queue_destroy(void)
{
  z_free_queue_free(freeq);
  freeq = NULL;
}

void
z_free_queue_add(gpointer f, ZFreeFunc freefn)
{
  z_free_queue_add_item(freeq, f, freefn);
}

void
z_free_queue_flush(void)
{
  z_free_queue_flush_items(freeq);
}
