/*
   Copyright (c) 2009, 2011, Monty Program Ab

   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; version 2 of the License.

   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

#include "maria_def.h"
#include "ma_servicethread.h"

/**
   Initializes the service thread

   @param control        control block

   @return Operation status
    @retval 0 OK
    @retval 1 error
*/

int ma_service_thread_control_init(MA_SERVICE_THREAD_CONTROL *control)
{
  int res= 0;
  DBUG_ENTER("ma_service_thread_control_init");
  DBUG_PRINT("init", ("control 0x%lx", (ulong) control));
  control->inited= TRUE;
  control->status= THREAD_DEAD; /* not yet born == dead */
  res= (mysql_mutex_init(key_SERVICE_THREAD_CONTROL_lock,
                         control->LOCK_control, MY_MUTEX_INIT_SLOW) ||
        mysql_cond_init(key_SERVICE_THREAD_CONTROL_cond,
                        control->COND_control, 0));
  DBUG_PRINT("info", ("init: %s", (res ? "Error" : "OK")));
  DBUG_RETURN(res);
}


/**
   Kill the service thread

   @param control        control block

   @note The service thread should react on condition and status equal
   THREAD_DYING, by setting status THREAD_DEAD, and issuing message to
   control thread via condition and exiting. The base way to do so is using
   my_service_thread_sleep() and my_service_thread_signal_end()
*/

void ma_service_thread_control_end(MA_SERVICE_THREAD_CONTROL *control)
{
  DBUG_ENTER("ma_service_thread_control_end");
  DBUG_PRINT("init", ("control 0x%lx", (ulong) control));
  DBUG_ASSERT(control->inited);
  mysql_mutex_lock(control->LOCK_control);
  if (control->status != THREAD_DEAD) /* thread was started OK */
  {
    DBUG_PRINT("info",("killing Maria background thread"));
    control->status= THREAD_DYING; /* kill it */
    do /* and wait for it to be dead */
    {
      /* wake it up if it was in a sleep */
      mysql_cond_broadcast(control->COND_control);
      DBUG_PRINT("info",("waiting for Maria background thread to die"));
      mysql_cond_wait(control->COND_control, control->LOCK_control);
    }
    while (control->status != THREAD_DEAD);
  }
  mysql_mutex_unlock(control->LOCK_control);
  mysql_mutex_destroy(control->LOCK_control);
  mysql_cond_destroy(control->COND_control);
  control->inited= FALSE;
  DBUG_VOID_RETURN;
}


/**
   Sleep for given number of nanoseconds with reaction on thread kill

   @param control        control block
   @param sleep_time     time of sleeping

   @return Operation status
    @retval FALSE Time out
    @retval TRUE  Thread should be killed
*/

my_bool my_service_thread_sleep(MA_SERVICE_THREAD_CONTROL *control,
                                ulonglong sleep_time)
{
  struct timespec abstime;
  my_bool res= FALSE;
  DBUG_ENTER("my_service_thread_sleep");
  DBUG_PRINT("init", ("control 0x%lx", (ulong) control));
  mysql_mutex_lock(control->LOCK_control);
  if (control->status == THREAD_DYING)
  {
    mysql_mutex_unlock(control->LOCK_control);
    DBUG_RETURN(TRUE);
  }
#if 0 /* good for testing, to do a lot of checkpoints, finds a lot of bugs */
  mysql_mutex_unlock(&control->LOCK_control);
  my_sleep(100000); /* a tenth of a second */
  mysql_mutex_lock(&control->LOCK_control);
#else
    /* To have a killable sleep, we use timedwait like our SQL GET_LOCK() */
  DBUG_PRINT("info", ("sleeping %llu nano seconds", sleep_time));
  if (sleep_time)
  {
    set_timespec_nsec(abstime, sleep_time);
    mysql_cond_timedwait(control->COND_control,
                           control->LOCK_control, &abstime);
  }
#endif
  if (control->status == THREAD_DYING)
    res= TRUE;
  mysql_mutex_unlock(control->LOCK_control);
  DBUG_RETURN(res);
}


/**
  inform about thread exiting

  @param control        control block
*/

void my_service_thread_signal_end(MA_SERVICE_THREAD_CONTROL *control)
{
  DBUG_ENTER("my_service_thread_signal_end");
  DBUG_PRINT("init", ("control 0x%lx", (ulong) control));
  mysql_mutex_lock(control->LOCK_control);
  control->status = THREAD_DEAD; /* indicate that we are dead */
  /*
    wake up ma_service_thread_control_end which may be waiting for
    our death
  */
  mysql_cond_broadcast(control->COND_control);
  /*
    broadcast was inside unlock because ma_service_thread_control_end
    destroys mutex
  */
  mysql_mutex_unlock(control->LOCK_control);
  DBUG_VOID_RETURN;
}
