/***************************************************************************
 *
 * 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: thread.c,v 1.9.2.2.2.5 2001/09/24 17:38:15 bazsi Exp $
 *
 * Author  : Bazsi
 * Auditor : kisza
 * Last version: 1.6
 * Notes:
 *
 ***************************************************************************/

#include <zorp/thread.h>
#include <zorp/log.h>
#include <string.h>

gint max_threads = 100;
gint num_threads = 0;
gint idle_threads = -1;
GAsyncQueue *queue;

static void
z_thread_free(ZThread *self)
{
#if 0
  g_static_mutex_lock(&threads_lock);
  threads = g_list_remove(threads, self);
  g_static_mutex_unlock(&threads_lock);
#endif
  g_free(self);
}

/*+ 
  A function wrapped around the real thread function logging two
  events when the thread starts & stops 
  +*/
static void
z_thread_func(gpointer st)
{
  ZThread *self = (ZThread *) st;
  
  do
    {
      self->thread = g_thread_self();
      /*LOG
        This message indicates that a thread is starting.
       */
      z_log(self->name, CORE_DEBUG, 6, "thread starting;");
      (*self->func)(self->arg);
      /*LOG
        This message indicates that a thread is ending.
       */
      z_log(self->name, CORE_DEBUG, 6, "thread exiting;");
      
      z_thread_free(self);
      self = NULL;
      g_async_queue_lock(queue);
      self = (ZThread *) g_async_queue_try_pop_unlocked(queue);
      if (!self)
        {
          num_threads--;
          g_async_queue_unref_and_unlock(queue);
        }
      else
        g_async_queue_unlock(queue);
    }
  while (self != NULL);
}

/*+

  Allocate and initialize a Zorp thread identified by a name, and
  using the given thread function.

  Parameters:
    name      name to identify this thread with
    func      thread function
    arg       pointer to pass to thread function

  Returns:
    ZThread instance

  +*/
void
z_thread_new(gchar *name, GThreadFunc func, gpointer arg)
{
  ZThread *self = g_new0(ZThread, 1);
  static gint thread_id = 1;
  
  self->thread_id = thread_id++;
  self->func = func;
  self->arg = arg;
  strncpy(self->name, name, sizeof(self->name) - 1);

  g_async_queue_lock(queue);
  if (num_threads >= max_threads)
    {
      z_log(NULL, CORE_DEBUG, 4, "Too many running threads, waiting for one to become free; num_threads='%d', max_threads='%d'", num_threads, max_threads);
      g_async_queue_push_unlocked(queue, self);
      g_async_queue_unlock(queue);
    }
  else
    {
      num_threads++;
      g_async_queue_ref_unlocked(queue);
      g_async_queue_unlock(queue);
      g_thread_create(z_thread_func, self, 0, 0, 0, G_THREAD_PRIORITY_NORMAL);
    }

  /* self->thread = g_thread_create(z_thread_func, self, 0, 0, 0, G_THREAD_PRIORITY_NORMAL); */
#if 0
  g_thread_pool_push(pool, self);
  g_static_mutex_lock(&threads_lock);
  threads = g_list_prepend(threads, self);
  g_static_mutex_unlock(&threads_lock);
#endif
}

void
z_thread_init(void)
{
  g_thread_init(NULL);
  queue = g_async_queue_new();
#if 0
  pool = g_thread_pool_new(z_thread_func, max_threads, 0, 0, 0, 0, NULL);
  if (idle_threads == -1)
    idle_threads = max_threads / 10;
  g_thread_pool_set_max_unused_threads(idle_threads);
#endif
}

void
z_thread_destroy(void)
{
  g_async_queue_unref(queue);
#if 0
  g_thread_pool_free(pool, TRUE, FALSE);
  pool = NULL;
#endif
}
