mirror of
				https://github.com/MariaDB/server.git
				synced 2025-11-04 04:46:15 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			844 lines
		
	
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			844 lines
		
	
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* Copyright (c) 2000, 2013, Oracle and/or its affiliates
 | 
						|
   Copyright (c) 2012, 2014, SkySQL 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335  USA */
 | 
						|
 | 
						|
/* To avoid problems with alarms in debug code, we disable DBUG here */
 | 
						|
#define FORCE_DBUG_OFF
 | 
						|
#include "mysys_priv.h"
 | 
						|
#include <my_global.h>
 | 
						|
 | 
						|
#if !defined(DONT_USE_THR_ALARM)
 | 
						|
#include <errno.h>
 | 
						|
#include <my_pthread.h>
 | 
						|
#include <signal.h>
 | 
						|
#include <my_sys.h>
 | 
						|
#include <m_string.h>
 | 
						|
#include <queues.h>
 | 
						|
#include "thr_alarm.h"
 | 
						|
 | 
						|
#ifdef HAVE_SYS_SELECT_H
 | 
						|
#include <sys/select.h>				/* AIX needs this for fd_set */
 | 
						|
#endif
 | 
						|
 | 
						|
#ifndef ETIME
 | 
						|
#define ETIME ETIMEDOUT
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef DBUG_OFF
 | 
						|
#define reset_index_in_queue(alarm_data)
 | 
						|
#else
 | 
						|
#define reset_index_in_queue(alarm_data) alarm_data->index_in_queue= 0;
 | 
						|
#endif /* DBUG_OFF */
 | 
						|
 | 
						|
#ifndef USE_ONE_SIGNAL_HAND
 | 
						|
#define one_signal_hand_sigmask(A,B,C) pthread_sigmask((A), (B), (C))
 | 
						|
#else
 | 
						|
#define one_signal_hand_sigmask(A,B,C)
 | 
						|
#endif
 | 
						|
 | 
						|
my_bool thr_alarm_inited= 0, my_disable_thr_alarm= 0;
 | 
						|
 | 
						|
#if !defined(_WIN32)
 | 
						|
 | 
						|
uint thr_client_alarm;
 | 
						|
static int alarm_aborted=1;			/* No alarm thread */
 | 
						|
volatile my_bool alarm_thread_running= 0;
 | 
						|
time_t next_alarm_expire_time= ~ (time_t) 0;
 | 
						|
static sig_handler process_alarm_part2(int sig);
 | 
						|
 | 
						|
static mysql_mutex_t LOCK_alarm;
 | 
						|
static mysql_cond_t COND_alarm;
 | 
						|
static sigset_t full_signal_set;
 | 
						|
static QUEUE alarm_queue;
 | 
						|
static uint max_used_alarms=0;
 | 
						|
pthread_t alarm_thread;
 | 
						|
 | 
						|
#define MY_THR_ALARM_QUEUE_EXTENT 10
 | 
						|
 | 
						|
#ifdef USE_ALARM_THREAD
 | 
						|
static void *alarm_handler(void *arg);
 | 
						|
#define reschedule_alarms() mysql_cond_signal(&COND_alarm)
 | 
						|
#else
 | 
						|
#define reschedule_alarms() pthread_kill(alarm_thread,THR_SERVER_ALARM)
 | 
						|
#endif
 | 
						|
 | 
						|
static sig_handler thread_alarm(int sig __attribute__((unused)));
 | 
						|
 | 
						|
static int compare_ulong(void *not_used __attribute__((unused)),
 | 
						|
                        const void *a_ptr, const void *b_ptr)
 | 
						|
{
 | 
						|
  const ulong *ap= a_ptr;
 | 
						|
  const ulong *bp= b_ptr;
 | 
						|
  return (*ap < *bp) ? -1  : (*ap == *bp) ? 0 : 1;
 | 
						|
}
 | 
						|
 | 
						|
void init_thr_alarm(uint max_alarms)
 | 
						|
{
 | 
						|
  sigset_t s;
 | 
						|
  DBUG_ENTER("init_thr_alarm");
 | 
						|
  alarm_aborted=0;
 | 
						|
  next_alarm_expire_time= ~ (time_t) 0;
 | 
						|
  init_queue(&alarm_queue, max_alarms+1, offsetof(ALARM,expire_time), 0,
 | 
						|
             compare_ulong, NullS, offsetof(ALARM, index_in_queue)+1,
 | 
						|
             MY_THR_ALARM_QUEUE_EXTENT);
 | 
						|
  sigfillset(&full_signal_set);			/* Neaded to block signals */
 | 
						|
  mysql_mutex_init(key_LOCK_alarm, &LOCK_alarm, MY_MUTEX_INIT_FAST);
 | 
						|
  mysql_cond_init(key_COND_alarm, &COND_alarm, NULL);
 | 
						|
  thr_client_alarm= SIGUSR1;
 | 
						|
  my_sigset(thr_client_alarm, thread_alarm);
 | 
						|
  sigemptyset(&s);
 | 
						|
  sigaddset(&s, THR_SERVER_ALARM);
 | 
						|
  alarm_thread=pthread_self();
 | 
						|
#if defined(USE_ALARM_THREAD)
 | 
						|
  {
 | 
						|
    pthread_attr_t thr_attr;
 | 
						|
    pthread_attr_init(&thr_attr);
 | 
						|
    pthread_attr_setscope(&thr_attr,PTHREAD_SCOPE_PROCESS);
 | 
						|
    pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED);
 | 
						|
    mysql_thread_create(key_thread_alarm,
 | 
						|
                        &alarm_thread, &thr_attr, alarm_handler, NULL);
 | 
						|
    pthread_attr_destroy(&thr_attr);
 | 
						|
  }
 | 
						|
#elif defined(USE_ONE_SIGNAL_HAND)
 | 
						|
  pthread_sigmask(SIG_BLOCK, &s, NULL);		/* used with sigwait() */
 | 
						|
#else
 | 
						|
  my_sigset(THR_SERVER_ALARM, process_alarm);
 | 
						|
  pthread_sigmask(SIG_UNBLOCK, &s, NULL);
 | 
						|
#endif
 | 
						|
  DBUG_VOID_RETURN;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void resize_thr_alarm(uint max_alarms)
 | 
						|
{
 | 
						|
  mysql_mutex_lock(&LOCK_alarm);
 | 
						|
  /*
 | 
						|
    It's ok not to shrink the queue as there may be more pending alarms than
 | 
						|
    than max_alarms
 | 
						|
  */
 | 
						|
  if (alarm_queue.elements < max_alarms)
 | 
						|
  {
 | 
						|
    resize_queue(&alarm_queue,max_alarms+1);
 | 
						|
    max_used_alarms= alarm_queue.elements;
 | 
						|
  }
 | 
						|
  mysql_mutex_unlock(&LOCK_alarm);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  Request alarm after sec seconds.
 | 
						|
 | 
						|
  SYNOPSIS
 | 
						|
    thr_alarm()
 | 
						|
    alrm		Pointer to alarm detection
 | 
						|
    alarm_data		Structure to store in alarm queue
 | 
						|
 | 
						|
  NOTES
 | 
						|
    This function can't be called from the alarm-handling thread.
 | 
						|
 | 
						|
  RETURN VALUES
 | 
						|
    0 ok
 | 
						|
    1 If no more alarms are allowed (aborted by process)
 | 
						|
 | 
						|
    Stores in first argument a pointer to a non-zero int which is set to 0
 | 
						|
    when the alarm has been given
 | 
						|
*/
 | 
						|
 | 
						|
my_bool thr_alarm(thr_alarm_t *alrm, uint sec, ALARM *alarm_data)
 | 
						|
{
 | 
						|
  time_t now, next;
 | 
						|
#ifndef USE_ONE_SIGNAL_HAND
 | 
						|
  sigset_t old_mask;
 | 
						|
#endif
 | 
						|
  my_bool reschedule;
 | 
						|
  struct st_my_thread_var *current_my_thread_var= my_thread_var;
 | 
						|
  DBUG_ENTER("thr_alarm");
 | 
						|
  DBUG_PRINT("enter",("thread: %s  sec: %d",my_thread_name(),sec));
 | 
						|
 | 
						|
  if (my_disable_thr_alarm)
 | 
						|
  {
 | 
						|
    (*alrm)= &alarm_data->alarmed;
 | 
						|
    alarm_data->alarmed= 1;                 /* Abort if interrupted */
 | 
						|
    DBUG_RETURN(0);
 | 
						|
  }
 | 
						|
 | 
						|
  if (unlikely(alarm_aborted))
 | 
						|
  {					/* No signal thread */
 | 
						|
    DBUG_PRINT("info", ("alarm aborted"));
 | 
						|
    if (alarm_aborted > 0)
 | 
						|
      goto abort_no_unlock;
 | 
						|
    sec= 1;					/* Abort mode */
 | 
						|
  }
 | 
						|
 | 
						|
  now= my_time(0);
 | 
						|
  if (!alarm_data)
 | 
						|
  {
 | 
						|
    if (!(alarm_data=(ALARM*) my_malloc(PSI_INSTRUMENT_ME, sizeof(ALARM),
 | 
						|
                                        MYF(MY_WME))))
 | 
						|
      goto abort_no_unlock;
 | 
						|
    alarm_data->malloced= 1;
 | 
						|
  }
 | 
						|
  else
 | 
						|
    alarm_data->malloced= 0;
 | 
						|
  next= now + sec;
 | 
						|
  alarm_data->expire_time= next;
 | 
						|
  alarm_data->alarmed=   0;
 | 
						|
  alarm_data->thread=    current_my_thread_var->pthread_self;
 | 
						|
  alarm_data->thread_id= current_my_thread_var->id;
 | 
						|
 | 
						|
  one_signal_hand_sigmask(SIG_BLOCK,&full_signal_set,&old_mask);
 | 
						|
  mysql_mutex_lock(&LOCK_alarm);        /* Lock from threads & alarms */
 | 
						|
  if (alarm_queue.elements >= max_used_alarms)
 | 
						|
  {
 | 
						|
    max_used_alarms=alarm_queue.elements+1;
 | 
						|
  }
 | 
						|
  reschedule= (ulong) next_alarm_expire_time > (ulong) next;
 | 
						|
  queue_insert_safe(&alarm_queue, (uchar*) alarm_data);
 | 
						|
  assert(alarm_data->index_in_queue > 0);
 | 
						|
 | 
						|
  /* Reschedule alarm if the current one has more than sec left */
 | 
						|
  if (unlikely(reschedule))
 | 
						|
  {
 | 
						|
    DBUG_PRINT("info", ("reschedule"));
 | 
						|
    if (pthread_equal(pthread_self(),alarm_thread))
 | 
						|
    {
 | 
						|
      alarm(sec);				/* purecov: inspected */
 | 
						|
      next_alarm_expire_time= next;
 | 
						|
    }
 | 
						|
    else
 | 
						|
      reschedule_alarms();			/* Reschedule alarms */
 | 
						|
  }
 | 
						|
  mysql_mutex_unlock(&LOCK_alarm);
 | 
						|
  one_signal_hand_sigmask(SIG_SETMASK,&old_mask,NULL);
 | 
						|
  (*alrm)= &alarm_data->alarmed;
 | 
						|
  DBUG_RETURN(0);
 | 
						|
 | 
						|
abort_no_unlock:
 | 
						|
  *alrm= 0;					/* No alarm */
 | 
						|
  DBUG_RETURN(1);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  Remove alarm from list of alarms
 | 
						|
*/
 | 
						|
 | 
						|
void thr_end_alarm(thr_alarm_t *alarmed)
 | 
						|
{
 | 
						|
  ALARM *alarm_data;
 | 
						|
#ifndef USE_ONE_SIGNAL_HAND
 | 
						|
  sigset_t old_mask;
 | 
						|
#endif
 | 
						|
  DBUG_ENTER("thr_end_alarm");
 | 
						|
 | 
						|
  if (my_disable_thr_alarm)
 | 
						|
    DBUG_VOID_RETURN;
 | 
						|
  one_signal_hand_sigmask(SIG_BLOCK,&full_signal_set,&old_mask);
 | 
						|
  alarm_data= (ALARM*) ((uchar*) *alarmed - offsetof(ALARM,alarmed));
 | 
						|
  mysql_mutex_lock(&LOCK_alarm);
 | 
						|
  DBUG_ASSERT(alarm_data->index_in_queue != 0);
 | 
						|
  DBUG_ASSERT((ALARM*) queue_element(&alarm_queue,
 | 
						|
                                     alarm_data->index_in_queue) ==
 | 
						|
              alarm_data);
 | 
						|
  queue_remove(&alarm_queue, alarm_data->index_in_queue);
 | 
						|
  mysql_mutex_unlock(&LOCK_alarm);
 | 
						|
  one_signal_hand_sigmask(SIG_SETMASK,&old_mask,NULL);
 | 
						|
  reset_index_in_queue(alarm_data);
 | 
						|
  DBUG_VOID_RETURN;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
  Come here when some alarm in queue is due.
 | 
						|
  Mark all alarms with are finnished in list.
 | 
						|
  Schedule alarms to be sent again after 1-10 sec (many alarms at once)
 | 
						|
  If alarm_aborted is set then all alarms are given and resent
 | 
						|
  every second.
 | 
						|
*/
 | 
						|
 | 
						|
sig_handler process_alarm(int sig __attribute__((unused)))
 | 
						|
{
 | 
						|
  sigset_t old_mask;
 | 
						|
/*
 | 
						|
  This must be first as we can't call DBUG inside an alarm for a normal thread
 | 
						|
*/
 | 
						|
 | 
						|
  /*
 | 
						|
    We have to do do the handling of the alarm in a sub function,
 | 
						|
    because otherwise we would get problems with two threads calling
 | 
						|
    DBUG_... functions at the same time (as two threads may call
 | 
						|
    process_alarm() at the same time
 | 
						|
  */
 | 
						|
 | 
						|
#ifndef USE_ALARM_THREAD
 | 
						|
  pthread_sigmask(SIG_SETMASK,&full_signal_set,&old_mask);
 | 
						|
  mysql_mutex_lock(&LOCK_alarm);
 | 
						|
#endif
 | 
						|
  process_alarm_part2(sig);
 | 
						|
#ifndef USE_ALARM_THREAD
 | 
						|
#if defined(SIGNAL_HANDLER_RESET_ON_DELIVERY) && !defined(USE_ONE_SIGNAL_HAND)
 | 
						|
  my_sigset(THR_SERVER_ALARM,process_alarm);
 | 
						|
#endif
 | 
						|
  mysql_mutex_unlock(&LOCK_alarm);
 | 
						|
  pthread_sigmask(SIG_SETMASK,&old_mask,NULL);
 | 
						|
#endif
 | 
						|
  return;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static sig_handler process_alarm_part2(int sig __attribute__((unused)))
 | 
						|
{
 | 
						|
  ALARM *alarm_data;
 | 
						|
  DBUG_ENTER("process_alarm");
 | 
						|
  DBUG_PRINT("info",("sig: %d  active alarms: %d",sig,alarm_queue.elements));
 | 
						|
 | 
						|
#if defined(MAIN) && !defined(__bsdi__)
 | 
						|
  printf("process_alarm\n"); fflush(stdout);
 | 
						|
#endif
 | 
						|
  if (likely(alarm_queue.elements))
 | 
						|
  {
 | 
						|
    if (unlikely(alarm_aborted))
 | 
						|
    {
 | 
						|
      uint i;
 | 
						|
      for (i= queue_first_element(&alarm_queue) ;
 | 
						|
           i <= queue_last_element(&alarm_queue) ;)
 | 
						|
      {
 | 
						|
	alarm_data=(ALARM*) queue_element(&alarm_queue,i);
 | 
						|
	alarm_data->alarmed=1;			/* Info to thread */
 | 
						|
	if (pthread_equal(alarm_data->thread,alarm_thread) ||
 | 
						|
	    pthread_kill(alarm_data->thread, thr_client_alarm))
 | 
						|
	{
 | 
						|
#ifdef MAIN
 | 
						|
	  printf("Warning: pthread_kill couldn't find thread!!!\n");
 | 
						|
#endif
 | 
						|
	  queue_remove(&alarm_queue,i);		/* No thread. Remove alarm */
 | 
						|
          reset_index_in_queue(alarm_data);
 | 
						|
	}
 | 
						|
	else
 | 
						|
	  i++;					/* Signal next thread */
 | 
						|
      }
 | 
						|
#ifndef USE_ALARM_THREAD
 | 
						|
      if (alarm_queue.elements)
 | 
						|
	alarm(1);				/* Signal soon again */
 | 
						|
#endif
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      time_t now= my_time(0);
 | 
						|
      time_t next= now+10-(now%10);
 | 
						|
      while ((alarm_data=(ALARM*) queue_top(&alarm_queue))->expire_time <= now)
 | 
						|
      {
 | 
						|
	alarm_data->alarmed=1;			/* Info to thread */
 | 
						|
	DBUG_PRINT("info",("sending signal to waiting thread"));
 | 
						|
	if (pthread_equal(alarm_data->thread,alarm_thread) ||
 | 
						|
	    pthread_kill(alarm_data->thread, thr_client_alarm))
 | 
						|
	{
 | 
						|
#ifdef MAIN
 | 
						|
	  printf("Warning: pthread_kill couldn't find thread!!!\n");
 | 
						|
#endif /* MAIN */
 | 
						|
	  queue_remove_top(&alarm_queue); /* No thread. Remove alarm */
 | 
						|
          reset_index_in_queue(alarm_data);
 | 
						|
	  if (!alarm_queue.elements)
 | 
						|
	    break;
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
	  alarm_data->expire_time=next;
 | 
						|
	  queue_replace_top(&alarm_queue);
 | 
						|
	}
 | 
						|
      }
 | 
						|
#ifndef USE_ALARM_THREAD
 | 
						|
      if (alarm_queue.elements)
 | 
						|
      {
 | 
						|
#ifdef __bsdi__
 | 
						|
	alarm(0);				/* Remove old alarm */
 | 
						|
#endif
 | 
						|
	alarm((uint) (alarm_data->expire_time-now));
 | 
						|
        next_alarm_expire_time= alarm_data->expire_time;
 | 
						|
      }
 | 
						|
#endif
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    /*
 | 
						|
      Ensure that next time we call thr_alarm(), we will schedule a new alarm
 | 
						|
    */
 | 
						|
    next_alarm_expire_time= ~(time_t) 0;
 | 
						|
  }
 | 
						|
  DBUG_VOID_RETURN;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  Schedule all alarms now and optionally free all structures
 | 
						|
 | 
						|
  SYNPOSIS
 | 
						|
    end_thr_alarm()
 | 
						|
      free_structures		Set to 1 if we should free memory used for
 | 
						|
				the alarm queue.
 | 
						|
				When we call this we should KNOW that there
 | 
						|
				is no active alarms
 | 
						|
  IMPLEMENTATION
 | 
						|
    Set alarm_abort to -1 which will change the behavior of alarms as follows:
 | 
						|
    - All old alarms will be rescheduled at once
 | 
						|
    - All new alarms will be rescheduled to one second
 | 
						|
*/
 | 
						|
 | 
						|
void end_thr_alarm(my_bool free_structures)
 | 
						|
{
 | 
						|
  DBUG_ENTER("end_thr_alarm");
 | 
						|
  if (alarm_aborted != 1)			/* If memory not freed */
 | 
						|
  {
 | 
						|
    mysql_mutex_lock(&LOCK_alarm);
 | 
						|
    DBUG_PRINT("info",("Rescheduling %d waiting alarms",alarm_queue.elements));
 | 
						|
    alarm_aborted= -1;				/* mark aborted */
 | 
						|
    if (alarm_queue.elements || (alarm_thread_running && free_structures))
 | 
						|
    {
 | 
						|
      if (pthread_equal(pthread_self(),alarm_thread))
 | 
						|
	alarm(1);				/* Shut down everything soon */
 | 
						|
      else
 | 
						|
	reschedule_alarms();
 | 
						|
    }
 | 
						|
    if (free_structures)
 | 
						|
    {
 | 
						|
      struct timespec abstime;
 | 
						|
 | 
						|
      DBUG_ASSERT(!alarm_queue.elements);
 | 
						|
 | 
						|
      /* Wait until alarm thread dies */
 | 
						|
      set_timespec(abstime, 10);		/* Wait up to 10 seconds */
 | 
						|
      while (alarm_thread_running)
 | 
						|
      {
 | 
						|
        int error= mysql_cond_timedwait(&COND_alarm, &LOCK_alarm, &abstime);
 | 
						|
	if (error == ETIME || error == ETIMEDOUT)
 | 
						|
	  break;				/* Don't wait forever */
 | 
						|
      }
 | 
						|
      delete_queue(&alarm_queue);
 | 
						|
      alarm_aborted= 1;
 | 
						|
      mysql_mutex_unlock(&LOCK_alarm);
 | 
						|
      if (!alarm_thread_running)              /* Safety */
 | 
						|
      {
 | 
						|
        mysql_mutex_destroy(&LOCK_alarm);
 | 
						|
        mysql_cond_destroy(&COND_alarm);
 | 
						|
      }
 | 
						|
    }
 | 
						|
    else
 | 
						|
      mysql_mutex_unlock(&LOCK_alarm);
 | 
						|
  }
 | 
						|
  DBUG_VOID_RETURN;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  Remove another thread from the alarm
 | 
						|
*/
 | 
						|
 | 
						|
void thr_alarm_kill(my_thread_id thread_id)
 | 
						|
{
 | 
						|
  uint i;
 | 
						|
  DBUG_ENTER("thr_alarm_kill");
 | 
						|
 | 
						|
  if (alarm_aborted)
 | 
						|
    return;
 | 
						|
  mysql_mutex_lock(&LOCK_alarm);
 | 
						|
  for (i= queue_first_element(&alarm_queue) ;
 | 
						|
       i <= queue_last_element(&alarm_queue);
 | 
						|
       i++)
 | 
						|
  {
 | 
						|
    ALARM *element= (ALARM*) queue_element(&alarm_queue,i);
 | 
						|
    if (element->thread_id == thread_id)
 | 
						|
    {
 | 
						|
      DBUG_PRINT("info", ("found thread; Killing it"));
 | 
						|
      element->expire_time= 0;
 | 
						|
      queue_replace(&alarm_queue, i);
 | 
						|
      reschedule_alarms();
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  mysql_mutex_unlock(&LOCK_alarm);
 | 
						|
  DBUG_VOID_RETURN;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void thr_alarm_info(ALARM_INFO *info)
 | 
						|
{
 | 
						|
  mysql_mutex_lock(&LOCK_alarm);
 | 
						|
  info->next_alarm_time= 0;
 | 
						|
  info->max_used_alarms= max_used_alarms;
 | 
						|
  if ((info->active_alarms=  alarm_queue.elements))
 | 
						|
  {
 | 
						|
    time_t now= my_time(0);
 | 
						|
    long time_diff;
 | 
						|
    ALARM *alarm_data= (ALARM*) queue_top(&alarm_queue);
 | 
						|
    time_diff= (long) (alarm_data->expire_time - now);
 | 
						|
    info->next_alarm_time= (ulong) (time_diff < 0 ? 0 : time_diff);
 | 
						|
  }
 | 
						|
  mysql_mutex_unlock(&LOCK_alarm);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
  This is here for thread to get interruptet from read/write/fcntl
 | 
						|
  ARGSUSED
 | 
						|
*/
 | 
						|
 | 
						|
 | 
						|
static sig_handler thread_alarm(int sig __attribute__((unused)))
 | 
						|
{
 | 
						|
#ifdef MAIN
 | 
						|
  printf("thread_alarm\n"); fflush(stdout);
 | 
						|
#endif
 | 
						|
#ifdef SIGNAL_HANDLER_RESET_ON_DELIVERY
 | 
						|
  my_sigset(sig,thread_alarm);		/* int. thread system calls */
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#ifdef HAVE_TIMESPEC_TS_SEC
 | 
						|
#define tv_sec ts_sec
 | 
						|
#define tv_nsec ts_nsec
 | 
						|
#endif
 | 
						|
 | 
						|
/* set up a alarm thread with uses 'sleep' to sleep between alarms */
 | 
						|
 | 
						|
#ifdef USE_ALARM_THREAD
 | 
						|
static void *alarm_handler(void *arg __attribute__((unused)))
 | 
						|
{
 | 
						|
  int error;
 | 
						|
  struct timespec abstime;
 | 
						|
#ifdef MAIN
 | 
						|
  puts("Starting alarm thread");
 | 
						|
#endif
 | 
						|
  my_thread_init();
 | 
						|
  alarm_thread_running= 1;
 | 
						|
  mysql_mutex_lock(&LOCK_alarm);
 | 
						|
  for (;;)
 | 
						|
  {
 | 
						|
    if (alarm_queue.elements)
 | 
						|
    {
 | 
						|
      time_t sleep_time,now= my_time(0);
 | 
						|
      if (alarm_aborted)
 | 
						|
	sleep_time=now+1;
 | 
						|
      else
 | 
						|
	sleep_time= ((ALARM*) queue_top(&alarm_queue))->expire_time;
 | 
						|
      if (sleep_time > now)
 | 
						|
      {
 | 
						|
	abstime.tv_sec=sleep_time;
 | 
						|
	abstime.tv_nsec=0;
 | 
						|
        next_alarm_expire_time= sleep_time;
 | 
						|
        if ((error= mysql_cond_timedwait(&COND_alarm, &LOCK_alarm, &abstime)) &&
 | 
						|
	    error != ETIME && error != ETIMEDOUT)
 | 
						|
	{
 | 
						|
#ifdef MAIN
 | 
						|
	  printf("Got error: %d from ptread_cond_timedwait (errno: %d)\n",
 | 
						|
		 error,errno);
 | 
						|
#endif
 | 
						|
	}
 | 
						|
      }
 | 
						|
    }
 | 
						|
    else if (alarm_aborted == -1)
 | 
						|
      break;
 | 
						|
    else
 | 
						|
    {
 | 
						|
      next_alarm_expire_time= ~ (time_t) 0;
 | 
						|
      if ((error= mysql_cond_wait(&COND_alarm, &LOCK_alarm)))
 | 
						|
      {
 | 
						|
#ifdef MAIN
 | 
						|
        printf("Got error: %d from ptread_cond_wait (errno: %d)\n",
 | 
						|
               error,errno);
 | 
						|
#endif
 | 
						|
      }
 | 
						|
    }
 | 
						|
    process_alarm(0);
 | 
						|
  }
 | 
						|
  bzero((char*) &alarm_thread,sizeof(alarm_thread)); /* For easy debugging */
 | 
						|
  alarm_thread_running= 0;
 | 
						|
  mysql_cond_signal(&COND_alarm);
 | 
						|
  mysql_mutex_unlock(&LOCK_alarm);
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
#endif /* USE_ALARM_THREAD */
 | 
						|
#endif
 | 
						|
 | 
						|
/****************************************************************************
 | 
						|
  Handling of test case (when compiled with -DMAIN)
 | 
						|
***************************************************************************/
 | 
						|
 | 
						|
#ifdef MAIN
 | 
						|
#if !defined(DONT_USE_THR_ALARM)
 | 
						|
 | 
						|
static mysql_cond_t COND_thread_count;
 | 
						|
static mysql_mutex_t LOCK_thread_count;
 | 
						|
static uint thread_count;
 | 
						|
 | 
						|
#ifdef HPUX10
 | 
						|
typedef int * fd_set_ptr;
 | 
						|
#else
 | 
						|
typedef fd_set * fd_set_ptr;
 | 
						|
#endif /* HPUX10 */
 | 
						|
 | 
						|
static void *test_thread(void *arg)
 | 
						|
{
 | 
						|
  int i,param=*((int*) arg),wait_time,retry;
 | 
						|
  time_t start_time;
 | 
						|
  thr_alarm_t got_alarm;
 | 
						|
  fd_set fd;
 | 
						|
  FD_ZERO(&fd);
 | 
						|
  my_thread_init();
 | 
						|
  printf("Thread %d (%s) started\n",param,my_thread_name()); fflush(stdout);
 | 
						|
  for (i=1 ; i <= 10 ; i++)
 | 
						|
  {
 | 
						|
    wait_time=param ? 11-i : i;
 | 
						|
    start_time= my_time(0);
 | 
						|
    if (thr_alarm(&got_alarm,wait_time,0))
 | 
						|
    {
 | 
						|
      printf("Thread: %s  Alarms aborted\n",my_thread_name());
 | 
						|
      break;
 | 
						|
    }
 | 
						|
    if (wait_time == 3)
 | 
						|
    {
 | 
						|
      printf("Thread: %s  Simulation of no alarm needed\n",my_thread_name());
 | 
						|
      fflush(stdout);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      for (retry=0 ; !thr_got_alarm(&got_alarm) && retry < 10 ; retry++)
 | 
						|
      {
 | 
						|
	printf("Thread: %s  Waiting %d sec\n",my_thread_name(),wait_time);
 | 
						|
	select(0,(fd_set_ptr) &fd,0,0,0);
 | 
						|
      }
 | 
						|
      if (!thr_got_alarm(&got_alarm))
 | 
						|
      {
 | 
						|
	printf("Thread: %s  didn't get an alarm. Aborting!\n",
 | 
						|
	       my_thread_name());
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      if (wait_time == 7)
 | 
						|
      {						/* Simulate alarm-miss */
 | 
						|
	fd_set readFDs;
 | 
						|
	uint max_connection=fileno(stdin);
 | 
						|
	FD_ZERO(&readFDs);
 | 
						|
	FD_SET(max_connection,&readFDs);
 | 
						|
	retry=0;
 | 
						|
	for (;;)
 | 
						|
	{
 | 
						|
	  printf("Thread: %s  Simulating alarm miss\n",my_thread_name());
 | 
						|
	  fflush(stdout);
 | 
						|
	  if (select(max_connection+1, (fd_set_ptr) &readFDs,0,0,0) < 0)
 | 
						|
	  {
 | 
						|
	    if (errno == EINTR)
 | 
						|
	      break;				/* Got new interrupt */
 | 
						|
	    printf("Got errno: %d from select.  Retrying..\n",errno);
 | 
						|
	    if (retry++ >= 3)
 | 
						|
	    {
 | 
						|
	      printf("Warning:  Interrupt of select() doesn't set errno!\n");
 | 
						|
	      break;
 | 
						|
	    }
 | 
						|
	  }
 | 
						|
	  else					/* This shouldn't happen */
 | 
						|
	  {
 | 
						|
	    if (!FD_ISSET(max_connection,&readFDs))
 | 
						|
	    {
 | 
						|
	      printf("Select interrupted, but errno not set\n");
 | 
						|
	      fflush(stdout);
 | 
						|
	      if (retry++ >= 3)
 | 
						|
		break;
 | 
						|
	      continue;
 | 
						|
	    }
 | 
						|
	    (void) getchar();			/* Somebody was playing */
 | 
						|
	  }
 | 
						|
	}
 | 
						|
      }
 | 
						|
    }
 | 
						|
    printf("Thread: %s  Slept for %d (%d) sec\n",my_thread_name(),
 | 
						|
	   (int) (my_time(0)-start_time), wait_time); fflush(stdout);
 | 
						|
    thr_end_alarm(&got_alarm);
 | 
						|
    fflush(stdout);
 | 
						|
  }
 | 
						|
  mysql_mutex_lock(&LOCK_thread_count);
 | 
						|
  thread_count--;
 | 
						|
  mysql_cond_signal(&COND_thread_count); /* Tell main we are ready */
 | 
						|
  mysql_mutex_unlock(&LOCK_thread_count);
 | 
						|
  my_thread_end();
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static void *signal_hand(void *arg __attribute__((unused)))
 | 
						|
{
 | 
						|
  sigset_t set;
 | 
						|
  int sig,error,err_count=0;;
 | 
						|
 | 
						|
  my_thread_init();
 | 
						|
  pthread_detach_this_thread();
 | 
						|
  init_thr_alarm(10);				/* Setup alarm handler */
 | 
						|
  mysql_mutex_lock(&LOCK_thread_count);         /* Required by bsdi */
 | 
						|
  mysql_cond_signal(&COND_thread_count);        /* Tell main we are ready */
 | 
						|
  mysql_mutex_unlock(&LOCK_thread_count);
 | 
						|
 | 
						|
  sigemptyset(&set);				/* Catch all signals */
 | 
						|
  sigaddset(&set,SIGINT);
 | 
						|
  sigaddset(&set,SIGQUIT);
 | 
						|
  sigaddset(&set,SIGTERM);
 | 
						|
  sigaddset(&set,SIGHUP);
 | 
						|
#ifdef SIGTSTP
 | 
						|
  sigaddset(&set,SIGTSTP);
 | 
						|
#endif
 | 
						|
#ifdef USE_ONE_SIGNAL_HAND
 | 
						|
  sigaddset(&set,THR_SERVER_ALARM);		/* For alarms */
 | 
						|
  puts("Starting signal and alarm handling thread");
 | 
						|
#else
 | 
						|
  puts("Starting signal handling thread");
 | 
						|
#endif
 | 
						|
  printf("server alarm: %d  thread alarm: %d\n",
 | 
						|
         THR_SERVER_ALARM, thr_client_alarm);
 | 
						|
  DBUG_PRINT("info",("Starting signal and alarm handling thread"));
 | 
						|
  for(;;)
 | 
						|
  {
 | 
						|
    int code;
 | 
						|
    while ((error=my_sigwait(&set,&sig,&code)) == EINTR)
 | 
						|
      printf("sigwait restarted\n");
 | 
						|
    if (error)
 | 
						|
    {
 | 
						|
      fprintf(stderr,"Got error %d from sigwait\n",error);
 | 
						|
      if (err_count++ > 5)
 | 
						|
	exit(1);				/* Too many errors in test */
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
#ifdef USE_ONE_SIGNAL_HAND
 | 
						|
    if (sig != THR_SERVER_ALARM)
 | 
						|
#endif
 | 
						|
      printf("Main thread: Got signal %d\n",sig);
 | 
						|
    switch (sig) {
 | 
						|
    case SIGINT:
 | 
						|
    case SIGQUIT:
 | 
						|
    case SIGTERM:
 | 
						|
    case SIGHUP:
 | 
						|
      printf("Aborting nicely\n");
 | 
						|
      end_thr_alarm(0);
 | 
						|
      break;
 | 
						|
#ifdef SIGTSTP
 | 
						|
    case SIGTSTP:
 | 
						|
      printf("Aborting\n");
 | 
						|
      exit(1);
 | 
						|
      return 0;					/* Keep some compilers happy */
 | 
						|
#endif
 | 
						|
#ifdef USE_ONE_SIGNAL_HAND
 | 
						|
     case THR_SERVER_ALARM:
 | 
						|
       process_alarm(sig);
 | 
						|
      break;
 | 
						|
#endif
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int main(int argc __attribute__((unused)),char **argv __attribute__((unused)))
 | 
						|
{
 | 
						|
  pthread_t tid;
 | 
						|
  pthread_attr_t thr_attr;
 | 
						|
  int i, param[2], error;
 | 
						|
  sigset_t set;
 | 
						|
  ALARM_INFO alarm_info;
 | 
						|
  MY_INIT(argv[0]);
 | 
						|
 | 
						|
  if (argc > 1 && argv[1][0] == '-' && argv[1][1] == '#')
 | 
						|
  {
 | 
						|
    DBUG_PUSH(argv[1]+2);
 | 
						|
  }
 | 
						|
  mysql_mutex_init(0, &LOCK_thread_count, MY_MUTEX_INIT_FAST);
 | 
						|
  mysql_cond_init(0, &COND_thread_count, NULL);
 | 
						|
 | 
						|
  /* Start a alarm handling thread */
 | 
						|
  sigemptyset(&set);
 | 
						|
  sigaddset(&set,SIGINT);
 | 
						|
  sigaddset(&set,SIGQUIT);
 | 
						|
  sigaddset(&set,SIGTERM);
 | 
						|
  sigaddset(&set,SIGHUP);
 | 
						|
  signal(SIGTERM,SIG_DFL);			/* If it's blocked by parent */
 | 
						|
#ifdef SIGTSTP
 | 
						|
  sigaddset(&set,SIGTSTP);
 | 
						|
#endif
 | 
						|
  sigaddset(&set,THR_SERVER_ALARM);
 | 
						|
  sigdelset(&set, thr_client_alarm);
 | 
						|
  (void) pthread_sigmask(SIG_SETMASK,&set,NULL);
 | 
						|
 | 
						|
  pthread_attr_init(&thr_attr);
 | 
						|
  pthread_attr_setscope(&thr_attr,PTHREAD_SCOPE_PROCESS);
 | 
						|
  pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED);
 | 
						|
  pthread_attr_setstacksize(&thr_attr,65536L);
 | 
						|
 | 
						|
  /* Start signal thread and wait for it to start */
 | 
						|
  mysql_mutex_lock(&LOCK_thread_count);
 | 
						|
  mysql_thread_create(0, &tid, &thr_attr, signal_hand, NULL);
 | 
						|
  mysql_cond_wait(&COND_thread_count, &LOCK_thread_count);
 | 
						|
  mysql_mutex_unlock(&LOCK_thread_count);
 | 
						|
  DBUG_PRINT("info",("signal thread created"));
 | 
						|
 | 
						|
  pthread_attr_setscope(&thr_attr,PTHREAD_SCOPE_PROCESS);
 | 
						|
  printf("Main thread: %s\n",my_thread_name());
 | 
						|
  for (i=0 ; i < 2 ; i++)
 | 
						|
  {
 | 
						|
    param[i]= i;
 | 
						|
    mysql_mutex_lock(&LOCK_thread_count);
 | 
						|
    if ((error= mysql_thread_create(0,
 | 
						|
                                    &tid, &thr_attr, test_thread,
 | 
						|
                                    (void*) ¶m[i])))
 | 
						|
    {
 | 
						|
      printf("Can't create thread %d, error: %d\n",i,error);
 | 
						|
      exit(1);
 | 
						|
    }
 | 
						|
    thread_count++;
 | 
						|
    mysql_mutex_unlock(&LOCK_thread_count);
 | 
						|
  }
 | 
						|
 | 
						|
  pthread_attr_destroy(&thr_attr);
 | 
						|
  mysql_mutex_lock(&LOCK_thread_count);
 | 
						|
  thr_alarm_info(&alarm_info);
 | 
						|
  printf("Main_thread:  Alarms: %u  max_alarms: %u  next_alarm_time: %lu\n",
 | 
						|
	 alarm_info.active_alarms, alarm_info.max_used_alarms,
 | 
						|
	 alarm_info.next_alarm_time);
 | 
						|
  while (thread_count)
 | 
						|
  {
 | 
						|
    mysql_cond_wait(&COND_thread_count, &LOCK_thread_count);
 | 
						|
    if (thread_count == 1)
 | 
						|
    {
 | 
						|
      printf("Calling end_thr_alarm. This should cancel the last thread\n");
 | 
						|
      end_thr_alarm(0);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  mysql_mutex_unlock(&LOCK_thread_count);
 | 
						|
  thr_alarm_info(&alarm_info);
 | 
						|
  end_thr_alarm(1);
 | 
						|
  printf("Main_thread:  Alarms: %u  max_alarms: %u  next_alarm_time: %lu\n",
 | 
						|
	 alarm_info.active_alarms, alarm_info.max_used_alarms,
 | 
						|
	 alarm_info.next_alarm_time);
 | 
						|
  printf("Test succeeded\n");
 | 
						|
  mysql_cond_destroy(&COND_thread_count);
 | 
						|
  mysql_mutex_destroy(&LOCK_thread_count);
 | 
						|
  my_end(MY_CHECK_ERROR);
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
#else /* !defined(DONT_USE_ALARM_THREAD) */
 | 
						|
 | 
						|
int main(int argc __attribute__((unused)),char **argv __attribute__((unused)))
 | 
						|
{
 | 
						|
  printf("thr_alarm disabled with DONT_USE_THR_ALARM\n");
 | 
						|
  exit(1);
 | 
						|
}
 | 
						|
 | 
						|
#endif /* !defined(DONT_USE_ALARM_THREAD) */
 | 
						|
#endif /* WIN */
 | 
						|
#endif /* MAIN */
 |